diff --git a/ibex_demo_system_core.core b/ibex_demo_system_core.core index bca488ee..da14cc6f 100644 --- a/ibex_demo_system_core.core +++ b/ibex_demo_system_core.core @@ -16,7 +16,13 @@ filesets: - rtl/system/gpio.sv - rtl/system/pwm.sv - rtl/system/pwm_wrapper.sv - - rtl/system/uart.sv + - rtl/system/uart_top.v + - rtl/system/uart_rx.v + - rtl/system/uart_tx.v + - rtl/system/sync_fifo.v + - rtl/system/async_fifo.v + - rtl/system/uart.vh + - rtl/system/synchronizer.v - rtl/system/spi_host.sv - rtl/system/spi_top.sv file_type: systemVerilogSource diff --git a/rtl/system/async_fifo.v b/rtl/system/async_fifo.v new file mode 100644 index 00000000..5f393bb7 --- /dev/null +++ b/rtl/system/async_fifo.v @@ -0,0 +1,101 @@ +`include "synchronizer.v" +`timescale 1ns/1ps +module async_fifo #( + parameter WIDTH = 32, // width of data bus + parameter DEPTH = 16 // depth of FIFO buffer +) ( + input wire [WIDTH-1:0] wdata_i, + input wire wclk_i, + input wire rclk_i, + input wire rst_ni, + input wire we_i, + input wire re_i, + output wire [WIDTH-1:0] rdata_o, + output wire full_o, + output wire empty_o, + output wire near_full_o, + output wire near_empty_o +); + +localparam ADDR_BITS = $clog2(DEPTH); // address width for buffer + +reg [ADDR_BITS:0] w_ptr, r_ptr; +wire [ADDR_BITS:0] w_ptr_gray, r_ptr_gray; + +wire [ADDR_BITS:0] w_ptr_next, r_ptr_next; +wire [ADDR_BITS:0] w_ptr_next_gray, r_ptr_next_gray; + +wire [ADDR_BITS-1:0] w_addr, r_addr; + +wire [ADDR_BITS:0] w_ptr_sync_gray, r_ptr_sync_gray; +reg [ADDR_BITS:0] w_ptr_sync_bin, r_ptr_sync_bin; + +reg [WIDTH-1:0] mem [0:DEPTH-1]; + +assign w_ptr_next = (we_i & ~full_o)? w_ptr + 1'b1 : w_ptr; +assign r_ptr_next = (re_i & ~empty_o)? r_ptr + 1'b1 : r_ptr; + +assign w_addr = w_ptr[ADDR_BITS-1:0]; +assign r_addr = r_ptr[ADDR_BITS-1:0]; + +assign w_ptr_gray = w_ptr ^ (w_ptr >> 1); +assign r_ptr_gray = r_ptr ^ (r_ptr >> 1); + +assign w_ptr_next_gray = w_ptr_next ^ (w_ptr_next >> 1); +assign r_ptr_next_gray = r_ptr_next ^ (r_ptr_next >> 1); + +sync_reg #( + .WIDTH(ADDR_BITS+1) +) sync_w2r_w_ptr ( + .clk_i(rclk_i), + .rst_ni(rst_ni), + .wdata_i(w_ptr_gray), + .rdata_o(w_ptr_sync_gray) +); + +sync_reg #( + .WIDTH(ADDR_BITS+1) +) sync_r2w_r_ptr ( + .clk_i(wclk_i), + .rst_ni(rst_ni), + .wdata_i(r_ptr_gray), + .rdata_o(r_ptr_sync_gray) +); + +integer i; +always @(*) begin : gray_to_bin_w_ptr + for(i=0; i> i); +end + +always @(*) begin : gray_to_bin_r_ptr + for(i=0; i> i); +end + +always @(posedge wclk_i, negedge rst_ni) + if(~rst_ni) + w_ptr <= {ADDR_BITS+1{1'b0}}; + else + w_ptr <= w_ptr_next; + +always @(posedge wclk_i, negedge rst_ni) begin : write + if(we_i & ~full_o) + mem[w_addr] <= wdata_i; +end + +always @(posedge rclk_i, negedge rst_ni) begin : read + if(~rst_ni) + r_ptr <= {ADDR_BITS+1{1'b0}}; + else + r_ptr <= r_ptr_next; +end + +assign full_o = w_ptr == {~r_ptr_sync_bin[ADDR_BITS], r_ptr_sync_bin[ADDR_BITS-1:0]}; +assign empty_o = r_ptr == w_ptr_sync_bin; +assign rdata_o = (empty_o)? {WIDTH{1'b0}} : mem[r_addr]; + +assign near_full_o = 1'b0; +assign near_empty_o = 1'b0; + +endmodule \ No newline at end of file diff --git a/rtl/system/ibex_demo_system.sv b/rtl/system/ibex_demo_system.sv index 167bbf57..82291453 100644 --- a/rtl/system/ibex_demo_system.sv +++ b/rtl/system/ibex_demo_system.sv @@ -88,7 +88,7 @@ module ibex_demo_system #( // interrupts logic timer_irq; - logic uart_irq; + logic [1:0] uart_irq; // host and device signals logic host_req [NrHosts]; @@ -268,7 +268,7 @@ module ibex_demo_system #( .irq_software_i(1'b0), .irq_timer_i (timer_irq), .irq_external_i(1'b0), - .irq_fast_i ({14'b0, uart_irq}), + .irq_fast_i ({13'b0, uart_irq}), .irq_nm_i (1'b0), .scramble_key_valid_i('0), @@ -349,23 +349,25 @@ module ibex_demo_system #( .pwm_o ); - uart #( - .ClockFrequency ( 50_000_000 ) + uart_top #( + .CLOCK_FREQUENCY (50_000_000), + .RX_FIFO_DEPTH (16), + .TX_FIFO_DEPTH (16) ) u_uart ( - .clk_i (clk_sys_i), - .rst_ni (rst_sys_ni), - - .device_req_i (device_req[Uart]), - .device_addr_i (device_addr[Uart]), - .device_we_i (device_we[Uart]), - .device_be_i (device_be[Uart]), - .device_wdata_i (device_wdata[Uart]), - .device_rvalid_o(device_rvalid[Uart]), - .device_rdata_o (device_rdata[Uart]), - - .uart_rx_i, - .uart_irq_o (uart_irq), - .uart_tx_o + .clk_i(clk_sys_i), // clock + .rst_ni(rst_sys_ni), // reset not + .we(device_we[Uart]), // write enable + .be(device_be[Uart]), // byte enable + .uart_wdata_i(device_wdata[Uart]), // data bus + .addr_i(device_addr[Uart]), // addr bus + .uart_req_i(device_req[Uart]), // request from core (IBEX LSU) + .uart_rx_i(uart_rx_i), // rx line + .uart_tx_o(uart_tx_o), // tx line + .uart_rdata_o(device_rdata[Uart]), // data bus + .uart_req_gnt_o(), // request granted to core (IBEX LSU) + .uart_rvalid_o(device_rvalid[Uart]), // request valid to core (IBEX LSU) + .uart_irq_o(uart_irq), // interrupt request (CSR) + .uart_err_o() // error to core (IBEX LSU) ); spi_top #( diff --git a/rtl/system/sync_fifo.v b/rtl/system/sync_fifo.v new file mode 100644 index 00000000..2e55890e --- /dev/null +++ b/rtl/system/sync_fifo.v @@ -0,0 +1,63 @@ +module sync_fifo #( + parameter WIDTH = 32, + parameter DEPTH = 128 +) ( + input wire clk_i, + input wire rst_ni, + input wire [WIDTH-1:0] wdata_i, + input wire we_i, + input wire re_i, + output wire [WIDTH-1:0] rdata_o, + output wire full_o, + output wire empty_o, + output wire near_full_o, + output wire near_empty_o +); + +localparam ADDR_BITS = $clog2(DEPTH); // address width for buffer + +reg [ADDR_BITS:0] w_ptr, r_ptr; +wire [ADDR_BITS:0] w_ptr_incr, r_ptr_incr; +wire [ADDR_BITS:0] w_ptr_next, r_ptr_next; + +wire [ADDR_BITS-1:0] w_addr, r_addr; + +wire full_next, empty_next; + +reg [WIDTH-1:0] mem [0:DEPTH-1]; + +assign w_ptr_incr = w_ptr + 1'b1; +assign r_ptr_incr = r_ptr + 1'b1; + +assign w_ptr_next = (~full_o & we_i)? w_ptr_incr : w_ptr; +assign r_ptr_next = (~empty_o & re_i)? r_ptr_incr : r_ptr; + +assign full_o = r_ptr == {~w_ptr[ADDR_BITS], w_ptr[ADDR_BITS-1:0]}; +assign empty_o = r_ptr == w_ptr; + +assign near_full_o = r_ptr == {~w_ptr_incr[ADDR_BITS], w_ptr_incr[ADDR_BITS-1:0]+1'b1}; +assign near_empty_o = r_ptr_incr == w_ptr; + +assign rdata_o = (empty_o)? {WIDTH{1'b0}} : mem[r_addr]; + +assign w_addr = w_ptr[ADDR_BITS-1:0]; +assign r_addr = r_ptr[ADDR_BITS-1:0]; + +always @(posedge clk_i, negedge rst_ni) begin + if(~rst_ni) begin + w_ptr <= {ADDR_BITS+1{1'b0}}; + r_ptr <= {ADDR_BITS+1{1'b0}}; + end + else begin + w_ptr <= w_ptr_next; + r_ptr <= r_ptr_next; + end +end + +always @(posedge clk_i) begin + if(we_i & ~full_o) begin + mem[w_addr] <= wdata_i; + end +end + +endmodule \ No newline at end of file diff --git a/rtl/system/synchronizer.v b/rtl/system/synchronizer.v new file mode 100644 index 00000000..a95f289b --- /dev/null +++ b/rtl/system/synchronizer.v @@ -0,0 +1,25 @@ +/* +* 2 DFF sync clock domain crossing +* for async fifo queue +*/ +`timescale 1ns/1ps +module sync_reg #( + parameter WIDTH = 32 +) ( + input wire clk_i, + input wire rst_ni, + input wire [WIDTH-1:0] wdata_i, + output reg [WIDTH-1:0] rdata_o +); + +reg [WIDTH-1:0] p0; + +always @(posedge clk_i, negedge rst_ni) + if(~rst_ni) begin + p0 <= 0; + rdata_o <= 0; + end + else + {rdata_o, p0} <= {p0, wdata_i}; + +endmodule \ No newline at end of file diff --git a/rtl/system/uart.sv b/rtl/system/uart.sv deleted file mode 100644 index 4c99e1b7..00000000 --- a/rtl/system/uart.sv +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - -module uart #( - parameter int unsigned ClockFrequency = 50_000_000, - parameter int unsigned BaudRate = 115_200, - parameter int unsigned RxFifoDepth = 128, - parameter int unsigned TxFifoDepth = 128 -) ( - input logic clk_i, - input logic rst_ni, - - input logic device_req_i, - /* verilator lint_off UNUSED */ - input logic [31:0] device_addr_i, - input logic device_we_i, - input logic [3:0] device_be_i, - input logic [31:0] device_wdata_i, - output logic device_rvalid_o, - output logic [31:0] device_rdata_o, - - input logic uart_rx_i, - output logic uart_irq_o, - output logic uart_tx_o -); - - localparam int unsigned ClocksPerBaud = ClockFrequency / BaudRate; - /* verilator lint_off WIDTH */ - localparam int unsigned UART_RX_REG = 32'h0; - /* verilator lint_off WIDTH */ - localparam int unsigned UART_TX_REG = 32'h4; - /* verilator lint_off WIDTH */ - localparam int unsigned UART_STATUS_REG = 32'h8; - - typedef enum logic[1:0] { - IDLE, - START, - PROC, - STOP - } uart_state_t; - - logic [31:0] device_rdata_d, device_rdata_q; - logic device_rvalid_d, device_rvalid_q; - - logic [11:0] reg_addr; - - logic [$clog2(ClocksPerBaud)-1:0] rx_baud_counter_q, rx_baud_counter_d; - logic rx_baud_tick; - - uart_state_t rx_state_q, rx_state_d; - logic [2:0] rx_bit_counter_q, rx_bit_counter_d; - logic [7:0] rx_current_byte_q, rx_current_byte_d; - logic [2:0] rx_q; - logic rx_start, rx_valid; - - logic rx_fifo_wvalid; - logic rx_fifo_rready; - logic [7:0] rx_fifo_rdata; - logic rx_fifo_rvalid; - logic rx_fifo_empty; - - logic [$clog2(ClocksPerBaud)-1:0] tx_baud_counter_q, tx_baud_counter_d; - logic tx_baud_tick; - - logic write_req; - - uart_state_t tx_state_q, tx_state_d; - logic [2:0] tx_bit_counter_q, tx_bit_counter_d; - logic [7:0] tx_current_byte_q, tx_current_byte_d; - logic tx_next_byte; - - logic tx_fifo_wvalid; - logic tx_fifo_rvalid, tx_fifo_rready; - logic [7:0] tx_fifo_rdata; - logic tx_fifo_full; - - assign reg_addr = device_addr_i[11:0]; - - always_comb begin - device_rdata_d = '0; - device_rvalid_d = 1'b0; - rx_fifo_rready = 1'b0; - - if (device_req_i) begin - device_rvalid_d = 1'b1; - - if (device_be_i[0] & ~device_we_i) begin - case (reg_addr) - UART_RX_REG: begin - device_rdata_d = {24'b0, rx_fifo_rdata}; - rx_fifo_rready = 1'b1; - end - UART_TX_REG: begin - device_rdata_d = '0; - end - UART_STATUS_REG: begin - device_rdata_d = {30'b0, tx_fifo_full, rx_fifo_empty}; - end - default: begin - device_rdata_d = '0; - end - endcase - end - end - end - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - device_rdata_q <= '0; - device_rvalid_q <= 1'b0; - end else begin - device_rdata_q <= device_rdata_d; - device_rvalid_q <= device_rvalid_d; - end - end - - assign device_rdata_o = device_rdata_q; - assign device_rvalid_o = device_rvalid_q; - - assign rx_fifo_wvalid = rx_baud_tick & rx_valid; - assign rx_fifo_empty = ~rx_fifo_rvalid; - - // Set the rx_baud_counter half-way on rx_start to ensure sampling the bits 'in the middle' - assign rx_baud_counter_d = rx_baud_tick ? '0 : - rx_start ? $bits(rx_baud_counter_q)'(ClocksPerBaud >> 1) : - rx_baud_counter_q + 1'b1; - - assign rx_baud_tick = rx_baud_counter_q == $bits(rx_baud_counter_q)'(ClocksPerBaud - 1); - - prim_fifo_sync #( - .Width(8), - .Pass (1'b0), - .Depth(RxFifoDepth) - ) u_rx_fifo ( - .clk_i, - .rst_ni, - .clr_i (1'b0), - - .wvalid_i(rx_fifo_wvalid), - .wready_o(), - .wdata_i (rx_current_byte_q), - - .rvalid_o(rx_fifo_rvalid), - .rready_i(rx_fifo_rready), - .rdata_o (rx_fifo_rdata), - - .full_o (), - .depth_o(), - .err_o () - ); - - assign uart_irq_o = !rx_fifo_empty; - - // Synchronize RX and derive rx_start signal - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - rx_q <= '0; - end else begin - rx_q <= {rx_q[1:0], uart_rx_i}; - end - end - - assign rx_start = !rx_q[1] & rx_q[2] & (rx_state_q == IDLE); - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - rx_baud_counter_q <= '0; - end else begin - rx_baud_counter_q <= rx_baud_counter_d; - end - end - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - rx_state_q <= IDLE; - rx_bit_counter_q <= '0; - rx_current_byte_q <= '0; - // Transition the rx state on both rx_start and an rx_baud_tick - end else if (rx_start || rx_baud_tick) begin - rx_state_q <= rx_state_d; - rx_bit_counter_q <= rx_bit_counter_d; - rx_current_byte_q <= rx_current_byte_d; - end - end - - always_comb begin - rx_valid = 0; - rx_bit_counter_d = rx_bit_counter_q; - rx_current_byte_d = rx_current_byte_q; - rx_state_d = rx_state_q; - - case (rx_state_q) - IDLE: begin - - if (rx_start) begin - rx_state_d = START; - end - end - START: begin - rx_current_byte_d = '0; - rx_bit_counter_d = '0; - - if (!rx_q[2]) begin - rx_state_d = PROC; - end else begin - rx_state_d = IDLE; - end - end - PROC: begin - rx_current_byte_d = {rx_q[2], rx_current_byte_q[7:1]}; - - if (rx_bit_counter_q == 3'd7) begin - rx_state_d = STOP; - end else begin - rx_bit_counter_d = rx_bit_counter_q + 3'd1; - end - end - STOP: begin - if (rx_q[2]) begin - rx_valid = 1; - end - rx_state_d = IDLE; - end - endcase - end - - assign write_req = (device_req_i & device_be_i[0] & device_we_i); - - assign tx_fifo_wvalid = (reg_addr == UART_TX_REG) & write_req; - assign tx_fifo_rready = tx_baud_tick & tx_next_byte; - - assign tx_baud_counter_d = tx_baud_tick ? '0 : tx_baud_counter_q + 1'b1; - assign tx_baud_tick = tx_baud_counter_q == $bits(tx_baud_counter_q)'(ClocksPerBaud - 1); - - prim_fifo_sync #( - .Width(8), - .Pass (1'b0), - .Depth(TxFifoDepth) - ) u_tx_fifo ( - .clk_i, - .rst_ni, - .clr_i (1'b0), - - .wvalid_i(tx_fifo_wvalid), - .wready_o(), - .wdata_i (device_wdata_i[7:0]), - - .rvalid_o(tx_fifo_rvalid), - .rready_i(tx_fifo_rready), - .rdata_o (tx_fifo_rdata), - - .full_o (tx_fifo_full), - .depth_o(), - .err_o () - ); - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - tx_baud_counter_q <= '0; - end else begin - tx_baud_counter_q <= tx_baud_counter_d; - end - end - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - tx_state_q <= IDLE; - tx_bit_counter_q <= '0; - tx_current_byte_q <= '0; - end else if (tx_baud_tick) begin - tx_state_q <= tx_state_d; - tx_bit_counter_q <= tx_bit_counter_d; - tx_current_byte_q <= tx_current_byte_d; - end - end - - always_comb begin - uart_tx_o = 1'b0; - tx_bit_counter_d = tx_bit_counter_q; - tx_current_byte_d = tx_current_byte_q; - tx_next_byte = 1'b0; - tx_state_d = tx_state_q; - - case (tx_state_q) - IDLE: begin - uart_tx_o = 1'b1; - - if (tx_fifo_rvalid) begin - tx_state_d = START; - end - end - START: begin - uart_tx_o = 1'b0; - tx_state_d = PROC; - tx_bit_counter_d = 3'd0; - tx_current_byte_d = tx_fifo_rdata; - tx_next_byte = 1'b1; - end - PROC: begin - uart_tx_o = tx_current_byte_q[0]; - - tx_current_byte_d = {1'b0, tx_current_byte_q[7:1]}; - if (tx_bit_counter_q == 3'd7) begin - tx_state_d = STOP; - end else begin - tx_bit_counter_d = tx_bit_counter_q + 3'd1; - end - end - STOP: begin - uart_tx_o = 1'b1; - if (tx_fifo_rvalid) begin - tx_state_d = START; - end else begin - tx_state_d = IDLE; - end - end - endcase - end - -endmodule diff --git a/rtl/system/uart.vh b/rtl/system/uart.vh new file mode 100644 index 00000000..bf28e11c --- /dev/null +++ b/rtl/system/uart.vh @@ -0,0 +1,12 @@ +`ifndef UART +`define UART + +`include "sync_fifo.v" +`include "async_fifo.v" +`include "uart_rx.v" +`include "uart_tx.v" + +// If need be to use asynchronous FIFOs, uncomment. +//`define ASYNC + +`endif \ No newline at end of file diff --git a/rtl/system/uart_rx.v b/rtl/system/uart_rx.v new file mode 100644 index 00000000..9254acdd --- /dev/null +++ b/rtl/system/uart_rx.v @@ -0,0 +1,123 @@ +module uart_rx ( + input wire clk_i, + input wire clk_en_i, + input wire rst_ni, + input wire en, + input wire rx_i, + input wire [3:0] data_size_i, + input wire parity_size_i, + input wire parity_type_i, + input wire [1:0] stop_size_i, + output wire [8:0] data_o, + output wire rx_rdy_o, + output wire rx_err_o, + output wire [1:0] rx_state_o +); + +localparam IDLE = 0; +localparam DATA = 1; +localparam PARITY = 2; +localparam STOP = 3; + +reg [3:0] data_counter; +reg [3:0] data_size; +reg parity_counter; +reg parity_size; +reg [1:0] stop_counter; +reg [1:0] stop_size; +reg [8:0] data_buf; +reg parity_buf; +reg parity_type; +reg [1:0] stop_buf; +reg [1:0] state; +reg [1:0] next_state; + +reg [8:0] data_d; +reg rx_rdy_d; +reg rx_err_d; + +always @(*) begin + case(data_size) + 6 : data_d <= {3'b0, data_buf[8:3]}; + 7 : data_d <= {2'b0, data_buf[8:2]}; + 8 : data_d <= {1'b0, data_buf[8:1]}; + 9 : data_d <= data_buf; + default : data_d <= data_buf; + endcase +end + +always @(*) begin + case(state) + IDLE : begin + rx_rdy_d <= 1'b0; + rx_err_d <= 1'b0; + end + DATA : begin + rx_rdy_d <= rx_rdy_d; + rx_err_d <= rx_err_d; + end + PARITY : begin + rx_rdy_d <= rx_rdy_d; + rx_err_d <= rx_err_d; + end + STOP : begin + rx_rdy_d <= ~|stop_counter; + rx_err_d <= (|parity_size) & ((^{data_buf, parity_buf} & ~parity_type) | + (~^{data_buf, parity_buf} & parity_type)); + end + endcase +end + +always @(posedge clk_i, negedge rst_ni) begin + if(~rst_ni) begin + state <= IDLE; + end + else if(clk_en_i) begin + state <= next_state; + case(state) + IDLE : begin + data_counter <= data_size_i - 1; + parity_counter <= parity_size_i - 1; + stop_counter <= stop_size_i - 1; + + data_size <= data_size_i; + parity_size <= parity_size_i; + parity_type <= parity_type_i; + stop_size <= stop_size_i; + + data_buf <= 0; + parity_buf <= 0; + stop_buf <= 0; + end + DATA : begin + data_counter <= data_counter - 1; + data_buf <= {rx_i, data_buf[8:1]}; + end + PARITY : begin + parity_counter <= parity_counter - 1; + parity_buf <= rx_i; + end + STOP : begin + stop_counter <= stop_counter - 1; + stop_buf <= {rx_i, stop_buf[1]}; + end + endcase + end +end + +always @ (*) begin + case(state) + IDLE : next_state <= (~rx_i & en)? DATA : IDLE; + DATA : next_state <= (|data_counter)? DATA : (|(parity_size))? PARITY : STOP; + PARITY : next_state <= (|parity_counter)? PARITY : STOP; + STOP : next_state <= (|stop_counter)? STOP : IDLE; + default : next_state <= IDLE; + endcase +end + +assign data_o = data_d; +assign rx_rdy_o = rx_rdy_d; +assign rx_err_o = rx_err_d; +assign rx_state_o = state; + +endmodule \ No newline at end of file diff --git a/rtl/system/uart_top.v b/rtl/system/uart_top.v new file mode 100644 index 00000000..2ef9690b --- /dev/null +++ b/rtl/system/uart_top.v @@ -0,0 +1,354 @@ +`include "uart.vh" + +module uart_top #( + parameter CLOCK_FREQUENCY = 50_000_000, + parameter RX_FIFO_DEPTH = 128, + parameter TX_FIFO_DEPTH = 128 +) ( + input wire clk_i, // core clock +`ifdef ASYNC + input wire clk_M_i, // master clock +`endif + input wire rst_ni, // reset not + input wire we, // write enable + input wire [3:0] be, // byte enable + input wire [31:0] uart_wdata_i, // data bus + input wire [31:0] addr_i, // addr bus + input wire uart_req_i, // request from core (IBEX LSU) + input wire uart_rx_i, // rx line + output wire uart_tx_o, // tx line + output wire [31:0] uart_rdata_o,// data bus + output wire uart_req_gnt_o, // request granted to core (IBEX LSU) + output wire uart_rvalid_o, // request valid to core (IBEX LSU) + output wire [1:0] uart_irq_o, // interrupt request (CSR) + output wire uart_err_o // error to core (IBEX LSU) +); + +reg [31:0] uart_rdata_q, uart_rdata_d; +reg uart_rvalid_q, uart_rvalid_d; +reg uart_err_q, uart_err_d; + +localparam [31:0] INSTRUCTION_SIZE = 5; // size of instruction in bits +localparam [INSTRUCTION_SIZE-1:0] OP_WRITE_UART_PARAMETERS = 5'b10000; // set protocol parameters +localparam [INSTRUCTION_SIZE-1:0] OP_WRITE_UART_EN = 5'b01000; // write data from bus to enable or diable rx/tx sub-modules +localparam [INSTRUCTION_SIZE-1:0] OP_READ_UART_STATE = 5'b00100; // read uart module state +localparam [INSTRUCTION_SIZE-1:0] OP_WRITE_UART_DATA = 5'b00010; // write data from bus to FIFO tx queue +localparam [INSTRUCTION_SIZE-1:0] OP_READ_UART_DATA = 5'b00001; // read data from FIFO rx queue +localparam [INSTRUCTION_SIZE-1:0] OP_DO_NOTHING = 5'b00000; // do nothing +reg [INSTRUCTION_SIZE-1:0] opcode; // instruction to execute + +reg [3:0] data_size; // data size in bits of protocol +reg parity_size; // parity of protocol (0 or 1 parity bits) +reg parity_type; // parity type of protocol (ODD (1) or EVEN (0)) +reg [1:0] stop_size; // stop size in bits of protocol (1 or 2) +reg [31:0] baud_rate; // baud rate of protocol (default 9600) + +wire [8:0] rx_data; // data received from serial +wire rx_rdy; // ready flag +wire rx_err; // error flag +reg rx_en; // enable register of rx module +wire [1:0] rx_state; // rx state machine status +wire rx_start; // rx start signal (stability improvment) +reg [2:0] rx_buf; // rx buffer + +wire rx_fq_re; // rx FIFO read enable +wire rx_fq_we; // rx FIFO write enable +wire [8:0] rx_fq_data_out; // rx FIFO data output +wire rx_fq_full; // rx FIFO is full flag +wire rx_fq_empty; // rx FIFO is empty flag +wire rx_fq_near_full; // rx FIFO is almost full +wire rx_fq_near_empty; // rx FIFO is almost empty + +wire tx_rdy; // ready flag +reg tx_en; // enable register of tx module +wire tx_state; // tx state machine status +wire tx_start; + +wire tx_fq_re; // tx FIFO queue read enable +wire tx_fq_we; // tx FIFO queue write enable +wire [8:0] tx_fq_data_out; // tx FIFO data output (to transmit) +wire tx_fq_full; // tx FIFO is full flag +wire tx_fq_empty; // tx FIFO is empty flag +wire tx_fq_near_full; // tx FIFO is almost full +wire tx_fq_near_empty; // tx FIFO is almost empty + +reg [31:0] rx_tick_counter; +reg rx_clk_en; +reg [31:0] tx_tick_counter; +reg tx_clk_en; + +uart_rx rx0 ( +`ifndef ASYNC + .clk_i(clk_i), // clock +`else + .clk_i(clk_M_i), // clock +`endif + .clk_en_i(rx_clk_en), // clock enable + .rst_ni(rst_ni), // negative reset signal + .en(rx_en), // rx enable + .rx_i(rx_buf[0]), // rx pin + .data_size_i(data_size), // data size (payload) + .parity_size_i(parity_size), // parity bit + .parity_type_i(parity_type), // parity type + .stop_size_i(stop_size), // stop bits size + .data_o(rx_data), // data out (received data) + .rx_rdy_o(rx_rdy), // received new data flag + .rx_err_o(rx_err), // parity mismatch flag + .rx_state_o(rx_state) +); + +`ifndef ASYNC +// Synchronous FIFO queue for rx module +sync_fifo #( + .WIDTH(9), // width of data bus + .DEPTH(RX_FIFO_DEPTH) // depth of FIFO buffer +) rx_sync_fifo ( + .clk_i(clk_i), // input clock + .rst_ni(rst_ni), // reset signal + .wdata_i(rx_data), // input data + .we_i(rx_fq_we), // write enable signal + .re_i(rx_fq_re), // read enable signal + .rdata_o(rx_fq_data_out), // output data + .full_o(rx_fq_full), // full flag + .empty_o(rx_fq_empty), // empty flag + .near_full_o(rx_fq_near_full), // near full flag + .near_empty_o(rx_fq_near_empty) // near empty flag +); +`else +// Asynchronous FIFO queue for rx module +async_fifo #( + .WIDTH(9), + .DEPTH(RX_FIFO_DEPTH) +) rx_async_fifo ( + .wclk_i(clk_M_i), + .rst_ni(rst_ni), + .wdata_i(rx_data), + .rclk_i(clk_i), + .we_i(rx_fq_we), + .re_i(rx_fq_re), + .rdata_o(rx_fq_data_out), + .full_o(rx_fq_full), + .empty_o(rx_fq_empty), + .near_full_o(rx_fq_near_full), + .near_empty_o(rx_fq_near_empty) +); +`endif + + +uart_tx tx0 ( +`ifndef ASYNC + .clk_i(clk_i), // clock +`else + .clk_i(clk_M_i), // clock +`endif + .clk_en_i(tx_clk_en), // clock enable + .rst_ni(rst_ni), // negative reset signal + .en(tx_en), // tx enable + .tx_start_i(tx_start), // start transmission flag + .data_size_i(data_size), // payload size + .parity_size_i(parity_size), // parity bit size + .parity_type_i(parity_type), // type of parity + .stop_size_i(stop_size), // stop bits size + .data_i(tx_fq_data_out), // data to transmit + .tx_o(uart_tx_o), // tx wire + .tx_rdy_o(tx_rdy), // module is ready flag + .tx_state_o(tx_state) +); + +`ifndef ASYNC +// Synchronous FIFO queue for tx module +sync_fifo #( + .WIDTH(9), // width of data bus + .DEPTH(TX_FIFO_DEPTH) // depth of FIFO buffer +) tx_sync_fifo ( + .clk_i(clk_i), // input clock + .rst_ni(rst_ni), // reset signal + .wdata_i(uart_wdata_i[8:0]), // input data + .we_i(tx_fq_we), // write enable signal + .re_i(tx_fq_re), // read enable signal + .rdata_o(tx_fq_data_out), // output data + .full_o(tx_fq_full), // full flag + .empty_o(tx_fq_empty), // empty flag + .near_full_o(tx_fq_near_full), // near full flag + .near_empty_o(tx_fq_near_empty) // near emtpy flag +); +`else +// Asynchronous FIFO queue for tx module +async_fifo #( + .WIDTH(9), + .DEPTH(TX_FIFO_DEPTH) +) tx_async_fifo ( + .wclk_i(clk_i), + .rst_ni(rst_ni), + .wdata_i(uart_wdata_i[8:0]), + .rclk_i(clk_M_i), + .we_i(tx_fq_we), + .re_i(tx_fq_re), + .rdata_o(tx_fq_data_out), + .full_o(tx_fq_full), + .empty_o(tx_fq_empty), + .near_full_o(rx_fq_near_full), + .near_empty_o(rx_fq_near_empty) +); +`endif + +assign rx_start = rx_buf[2] & ~rx_buf[1] & ~rx_buf[0] & rx_state == rx0.IDLE; + +assign tx_start = ~tx_fq_empty; +assign tx_fq_we = (opcode == OP_WRITE_UART_DATA) & tx_en ; +assign tx_fq_re = tx_rdy & tx_en & tx_clk_en; +assign rx_fq_we = rx_rdy & ~rx_err & rx_clk_en; +assign rx_fq_re = (opcode == OP_READ_UART_DATA) & rx_en; + +assign uart_irq_o = {tx_fq_near_full, ~rx_fq_empty}; + +always +`ifndef ASYNC + @(posedge clk_i, negedge rst_ni) +`else + @(posedge clk_M_i, negedge rst_ni) +`endif +begin + if(~rst_ni) begin + rx_buf <= 3'b111; + end + else begin + rx_buf <= {rx_buf[1:0], uart_rx_i}; + end +end + +always +`ifndef ASYNC + @(posedge clk_i, negedge rst_ni) +`else + @(posedge clk_M_i, negedge rst_ni) +`endif begin + if(~rst_ni) begin + rx_tick_counter <= 32'b0; + rx_clk_en <= 1'b0; + end + else begin + rx_tick_counter <= (rx_clk_en)? 32'b0 : (rx_start)? baud_rate >> 1 : rx_tick_counter + 1'b1; + rx_clk_en <= (rx_tick_counter == (baud_rate - 1'b1)); + end +end + + +always +`ifndef ASYNC + @(posedge clk_i, negedge rst_ni) +`else + @(posedge clk_M_i, negedge rst_ni) +`endif +begin + if(~rst_ni) begin + tx_tick_counter <= 32'b0; + tx_clk_en <= 1'b0; + end + else begin + tx_tick_counter <= (tx_clk_en)? 32'b0 : tx_tick_counter + 1'b1; + tx_clk_en <= (tx_tick_counter == (baud_rate - 1'b1)); + end +end + + +always @(*) begin + if(uart_req_i) + case(addr_i[7:0]) + 8'h00 : opcode <= (~we)? OP_READ_UART_DATA : OP_DO_NOTHING; + 8'h04 : opcode <= (we)? OP_WRITE_UART_DATA : OP_DO_NOTHING; + 8'h08 : opcode <= (~we)? OP_READ_UART_STATE : OP_DO_NOTHING; + 8'h0c : opcode <= (we)? OP_WRITE_UART_EN : OP_DO_NOTHING; + 8'h10 : opcode <= (we)? OP_WRITE_UART_PARAMETERS : OP_DO_NOTHING; + default : opcode <= OP_DO_NOTHING; + endcase + else + opcode <= OP_DO_NOTHING; +end + +always @(*) begin + uart_rvalid_d = 1'b1; + case(opcode) + OP_WRITE_UART_PARAMETERS : begin + uart_rdata_d <= 32'b0; + uart_err_d = 1'b0; + end + OP_READ_UART_DATA : begin + uart_rdata_d <= {23'b0, rx_fq_data_out}; + uart_err_d = rx_fq_empty; + end + OP_WRITE_UART_DATA : begin + uart_rdata_d <= 32'b0; + uart_err_d <= tx_fq_full; + end + OP_READ_UART_STATE : begin + uart_rdata_d <= {28'b0, tx_fq_near_full, rx_fq_near_empty, tx_fq_full, rx_fq_empty}; + uart_err_d = 1'b0; + end + OP_WRITE_UART_EN : begin + uart_rdata_d <= 32'b0; + end + default : begin + uart_rdata_d <= 32'b0; + uart_err_d = 1'b0; + end + endcase +end + +always @(posedge clk_i, negedge rst_ni) begin + if(~rst_ni) begin + uart_rdata_q <= 32'b0; + uart_rvalid_q <= 1'b0; + uart_err_q <= 1'b0; + end + else begin + uart_rdata_q <= uart_rdata_d; + uart_rvalid_q <= uart_rvalid_d; + uart_err_q <= uart_err_d; + end +end + +always @(posedge clk_i, negedge rst_ni) begin + if(~rst_ni) begin + baud_rate <= CLOCK_FREQUENCY / 9600; + data_size <= 4'h8; + parity_size <= 1'b0; + parity_type <= 1'b0; + stop_size <= 2'b01; + rx_en <= 1'b0; + tx_en <= 1'b0; + end + else begin + case(opcode) + OP_WRITE_UART_PARAMETERS : begin + case(uart_wdata_i[1:0]) + 2'b00 : data_size <= 4'h6; + 2'b01 : data_size <= 4'h7; + 2'b10 : data_size <= 4'h8; + 2'b11 : data_size <= 4'h9; + endcase + parity_size <= uart_wdata_i[2]; + parity_type <= uart_wdata_i[3]; + stop_size <= (uart_wdata_i[4])? 2'b10 : 2'b01; + case(uart_wdata_i[6:5]) + 2'b00 : baud_rate <= CLOCK_FREQUENCY / 4800; + 2'b01 : baud_rate <= CLOCK_FREQUENCY / 9600; + 2'b10 : baud_rate <= CLOCK_FREQUENCY / 57600; + 2'b11 : baud_rate <= CLOCK_FREQUENCY / 115200; + endcase + end + OP_WRITE_UART_EN : begin + {tx_en, rx_en} <= uart_wdata_i[1:0]; + end + default : begin + end + endcase + end +end + +assign uart_rdata_o = uart_rdata_q; +assign uart_rvalid_o = uart_rvalid_q; +assign uart_err_o = uart_err_q; +assign uart_req_gnt_o = 1'b1; + +endmodule \ No newline at end of file diff --git a/rtl/system/uart_tx.v b/rtl/system/uart_tx.v new file mode 100644 index 00000000..9215a566 --- /dev/null +++ b/rtl/system/uart_tx.v @@ -0,0 +1,71 @@ +module uart_tx ( + input wire clk_i, + input wire clk_en_i, + input wire rst_ni, + input wire en, + input wire tx_start_i, + input wire [3:0] data_size_i, + input wire parity_size_i, + input wire parity_type_i, + input wire [1:0] stop_size_i, + input wire [8:0] data_i, + output wire tx_o, + output wire tx_rdy_o, + output wire tx_state_o +); + +localparam IDLE = 0; // Waiting to send data +localparam WRITE = 1; // Transmitting frame + +reg [3:0] frame_counter; +reg [12:0] frame_buffer; +reg state; +reg next_state; + +always @ (posedge clk_i, negedge rst_ni) begin + if(~rst_ni) begin + state <= IDLE; + end + else if(clk_en_i) begin + state <= next_state; + case(state) + IDLE : begin + // Size of frame in bits + frame_counter <= stop_size_i + parity_size_i + data_size_i + 1 + 1; + // if none parity just fill with logic 1 + // else calculate appropriate parity + case(data_size_i) + 6 : begin + frame_buffer <= {4'b1111, (~|parity_size_i)? 1'b1 : (parity_type_i)? ^data_i : ~^data_i, data_i[5:0], 2'b01}; + end + 7 : begin + frame_buffer <= {3'b111, (~|parity_size_i)? 1'b1 : (parity_type_i)? ^data_i : ~^data_i, data_i[6:0], 2'b01}; + end + 8 : begin + frame_buffer <= {2'b11, (~|parity_size_i)? 1'b1 : (parity_type_i)? ^data_i : ~^data_i, data_i[7:0], 2'b01}; + end + default : begin + frame_buffer <= {1'b1, (~|parity_size_i)? 1'b1 : (parity_type_i)? ^data_i : ~^data_i, data_i, 2'b01}; + end + endcase + end + WRITE : begin + frame_counter <= frame_counter - 1; + frame_buffer <= {1'b1, frame_buffer[12:1]}; + end + endcase + end +end + +always @(*) + case(state) + IDLE : next_state <= (tx_start_i & en)? WRITE : IDLE; + WRITE : next_state <= (~|frame_counter)? IDLE : WRITE; + default : next_state <= IDLE; + endcase + +assign tx_rdy_o = (state == IDLE); +assign tx_o = (state == WRITE)? frame_buffer[0] : 1'b1; +assign tx_state_o = state; + +endmodule diff --git a/sw/c/common/demo_system.c b/sw/c/common/demo_system.c index ab7c4ff4..784ed70a 100644 --- a/sw/c/common/demo_system.c +++ b/sw/c/common/demo_system.c @@ -138,4 +138,3 @@ void simple_exc_handler(void) { while(1); } - diff --git a/sw/c/common/demo_system.h b/sw/c/common/demo_system.h index d2518b4a..517c6961 100644 --- a/sw/c/common/demo_system.h +++ b/sw/c/common/demo_system.h @@ -11,8 +11,12 @@ #include "uart.h" #include "gpio.h" -#define UART_IRQ_NUM 16 -#define UART_IRQ (1 << UART_IRQ_NUM) +#define UART_RX_IRQ_NUM 16 +#define UART_RX_IRQ (1 << UART_RX_IRQ_NUM) + +#define UART_TX_IRQ_NUM 17 +#define UART_TX_IRQ (1 << UART_TX_IRQ_NUM) + #define DEFAULT_UART UART_FROM_BASE_ADDR(UART0_BASE) #define GPIO_OUT GPIO_FROM_BASE_ADDR(GPIO_BASE + GPIO_OUT_REG) @@ -112,4 +116,4 @@ uint32_t get_mcycle(void); void reset_mcycle(void); -#endif +#endif \ No newline at end of file diff --git a/sw/c/common/uart.c b/sw/c/common/uart.c index 19edf7ef..97a8051d 100644 --- a/sw/c/common/uart.c +++ b/sw/c/common/uart.c @@ -1,13 +1,14 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - #include "uart.h" #include "dev_access.h" #include "demo_system.h" void uart_enable_rx_int(void) { - enable_interrupts(UART_IRQ); + enable_interrupts(UART_RX_IRQ); + set_global_interrupt_enable(1); +} + +void uart_enable_tx_int(void) { + enable_interrupts(UART_TX_IRQ); set_global_interrupt_enable(1); } @@ -26,3 +27,22 @@ void uart_out(uart_t uart, char c) { DEV_WRITE(uart + UART_TX_REG, c); } + +void uart_enable(uart_t uart, char en) { + + DEV_WRITE(uart + UART_ENABLE_REG, en); +} + +void uart_disable(uart_t uart) { + + DEV_WRITE(uart + UART_ENABLE_REG, 0x00); +} + +void uart_setup(uart_t uart, char parameters) { + + DEV_WRITE(uart + UART_PARAMETERS_REG, parameters); +} + +int uart_status(uart_t uart) { + return DEV_READ(DEFAULT_UART + UART_STATUS_REG); +} \ No newline at end of file diff --git a/sw/c/common/uart.h b/sw/c/common/uart.h index ac2578f6..40c195bf 100644 --- a/sw/c/common/uart.h +++ b/sw/c/common/uart.h @@ -1,16 +1,18 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - #ifndef UART_H__ #define UART_H__ -#define UART_RX_REG 0 -#define UART_TX_REG 4 -#define UART_STATUS_REG 8 +#include + +#define UART_RX_REG 0x00 +#define UART_TX_REG 0x04 +#define UART_STATUS_REG 0x08 +#define UART_ENABLE_REG 0x0C +#define UART_PARAMETERS_REG 0x10 -#define UART_STATUS_RX_EMPTY 1 -#define UART_STATUS_TX_FULL 2 +#define UART_STATUS_RX_EMPTY 0x01 +#define UART_STATUS_TX_FULL 0x02 +#define UART_STATUS_RX_NEAR_EMPTY 0x04 +#define UART_STATUS_TX_NEAR_FULL 0x08 #define UART_EOF -1 @@ -18,9 +20,37 @@ typedef void* uart_t; #define UART_FROM_BASE_ADDR(addr)((uart_t)(addr)) +#define DATA_SIZE_6 0x00 +#define DATA_SIZE_7 0x01 +#define DATA_SIZE_8 0x02 +#define DATA_SIZE_9 0x03 + +#define PARITY_NONE 0x00 +#define PARITY_EVEN 0x04 +#define PARITY_ODD 0x0C + +#define STOP_BITS_ONE 0x00 +#define STOP_BITS_TWO 0x10 + +#define BAUD_RATE_4800 0x00 +#define BAUD_RATE_9600 0x20 +#define BAUD_RATE_57600 0x40 +#define BAUD_RATE_115200 0x60 + +#define UART_RX_EN 0x01 +#define UART_TX_EN 0x02 + void uart_enable_rx_int(void); + +void uart_enable_tx_int(void); + int uart_in(uart_t uart); void uart_out(uart_t uart, char c); -#endif // UART_H__ +void uart_enable(uart_t uart, char en); +void uart_disable(uart_t uart); +void uart_setup(uart_t uart, char parameters); + +int uart_status(uart_t uart); +#endif diff --git a/sw/c/demo/hello_world/main.c b/sw/c/demo/hello_world/main.c index 4e612410..3d67492a 100644 --- a/sw/c/demo/hello_world/main.c +++ b/sw/c/demo/hello_world/main.c @@ -1,30 +1,43 @@ // Copyright lowRISC contributors. // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 - +#include "uart.h" #include "demo_system.h" #include "timer.h" #include "gpio.h" #include "pwm.h" #include +#include #define USE_GPIO_SHIFT_REG 0 -void test_uart_irq_handler(void) __attribute__((interrupt)); +void test_uart_rx_irq_handler(void) __attribute__((interrupt)); +void test_uart_tx_irq_handler(void) __attribute__((interrupt)); -void test_uart_irq_handler(void) { - int uart_in_char; - while ((uart_in_char = uart_in(DEFAULT_UART)) != -1) { - uart_out(DEFAULT_UART, uart_in_char); - uart_out(DEFAULT_UART, '\r'); - uart_out(DEFAULT_UART, '\n'); - } +void test_uart_rx_irq_handler(void) { + unsigned char u; + u = getchar(); + putchar(u); +} + +void test_uart_tx_irq_handler(void) { + volatile int i; + do + { + for(i=0; i < 100; i++){}; + } while (uart_status(DEFAULT_UART) & UART_STATUS_TX_NEAR_FULL); } int main(void) { - install_exception_handler(UART_IRQ_NUM, &test_uart_irq_handler); + install_exception_handler(UART_RX_IRQ_NUM, &test_uart_rx_irq_handler); + install_exception_handler(UART_TX_IRQ_NUM, &test_uart_tx_irq_handler); uart_enable_rx_int(); + uart_enable_tx_int(); + + + uart_enable(DEFAULT_UART, UART_RX_EN | UART_TX_EN); + uart_setup(DEFAULT_UART, DATA_SIZE_8 | PARITY_NONE | STOP_BITS_ONE | BAUD_RATE_115200); // This indicates how often the timer gets updated. timer_init(); @@ -41,37 +54,21 @@ int main(void) { bool ascending = true; // The three least significant bits correspond to RGB, where B is the leas significant. uint8_t color = 7; - + + const char str[] = " Hello World "; + uint32_t c = 0; while(1) { uint64_t cur_time = get_elapsed_time(); if (cur_time != last_elapsed_time) { last_elapsed_time = cur_time; - - // Disable interrupts whilst outputting to prevent output for RX IRQ - // happening in the middle + set_global_interrupt_enable(0); - - // Print this to UART (use the screen command to see it). - puts("Hello World! "); - puthex(last_elapsed_time); - puts(" Input Value: "); - uint32_t in_val = read_gpio(GPIO_IN_DBNC); - puthex(in_val); - putchar('\n'); - - // Re-enable interrupts with output complete + puts(str); + puthex(c++); + puts("\r\n"); set_global_interrupt_enable(1); - - // Cycling through green LEDs when BTN0 is pressed - if (USE_GPIO_SHIFT_REG) { - set_outputs(GPIO_OUT_SHIFT, in_val); - } else { - uint32_t out_val = read_gpio(GPIO_OUT); - out_val = ((out_val << 1) & GPIO_OUT_MASK) | (in_val & 0x1); - set_outputs(GPIO_OUT, out_val); - } - + // Going from bright to dim on PWM for(int i = 0; i < NUM_PWM_MODULES; i++) { set_pwm(PWM_FROM_ADDR_AND_INDEX(PWM_BASE, i), @@ -94,8 +91,8 @@ int main(void) { } } } + } - asm volatile ("wfi"); } }