/* Lattice Mico32 UART model. Contributed by Jon Beniston Copyright (C) 2009-2019 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "sim-main.h" #include "hw-main.h" #include "sim-assert.h" #include #include struct lm32uart { unsigned base; /* Base address of this UART. */ unsigned limit; /* Limit address of this UART. */ unsigned char rbr; unsigned char thr; unsigned char ier; unsigned char iir; unsigned char lcr; unsigned char mcr; unsigned char lsr; unsigned char msr; unsigned char div; struct hw_event *event; }; /* UART registers. */ #define LM32_UART_RBR 0x0 #define LM32_UART_THR 0x0 #define LM32_UART_IER 0x4 #define LM32_UART_IIR 0x8 #define LM32_UART_LCR 0xc #define LM32_UART_MCR 0x10 #define LM32_UART_LSR 0x14 #define LM32_UART_MSR 0x18 #define LM32_UART_DIV 0x1c #define LM32_UART_IER_RX_INT 0x1 #define LM32_UART_IER_TX_INT 0x2 #define MICOUART_IIR_TXRDY 0x2 #define MICOUART_IIR_RXRDY 0x4 #define LM32_UART_LSR_RX_RDY 0x01 #define LM32_UART_LSR_TX_RDY 0x20 #define LM32_UART_LCR_WLS_MASK 0x3 #define LM32_UART_LCR_WLS_5 0x0 #define LM32_UART_LCR_WLS_6 0x1 #define LM32_UART_LCR_WLS_7 0x2 #define LM32_UART_LCR_WLS_8 0x3 /* UART ports. */ enum { INT_PORT }; static const struct hw_port_descriptor lm32uart_ports[] = { {"int", INT_PORT, 0, output_port}, {} }; static void do_uart_tx_event (struct hw *me, void *data) { struct lm32uart *uart = hw_data (me); char c; /* Generate interrupt when transmission is complete. */ if (uart->ier & LM32_UART_IER_TX_INT) { /* Generate interrupt */ hw_port_event (me, INT_PORT, 1); } /* Indicate which interrupt has occured. */ uart->iir = MICOUART_IIR_TXRDY; /* Indicate THR is empty. */ uart->lsr |= LM32_UART_LSR_TX_RDY; /* Output the character in the THR. */ c = (char) uart->thr; /* WLS field in LCR register specifies the number of bits to output. */ switch (uart->lcr & LM32_UART_LCR_WLS_MASK) { case LM32_UART_LCR_WLS_5: c &= 0x1f; break; case LM32_UART_LCR_WLS_6: c &= 0x3f; break; case LM32_UART_LCR_WLS_7: c &= 0x7f; break; } printf ("%c", c); } static unsigned lm32uart_io_write_buffer (struct hw *me, const void *source, int space, unsigned_word base, unsigned nr_bytes) { struct lm32uart *uart = hw_data (me); int uart_reg; const unsigned char *source_bytes = source; int value = 0; HW_TRACE ((me, "write to 0x%08lx length %d with 0x%x", (long) base, (int) nr_bytes, value)); if (nr_bytes == 4) value = (source_bytes[0] << 24) | (source_bytes[1] << 16) | (source_bytes[2] << 8) | (source_bytes[3]); else hw_abort (me, "write of unsupported number of bytes: %d.", nr_bytes); uart_reg = base - uart->base; switch (uart_reg) { case LM32_UART_THR: /* Buffer the character to output. */ uart->thr = value; /* Indicate the THR is full. */ uart->lsr &= ~LM32_UART_LSR_TX_RDY; /* deassert interrupt when IER is loaded. */ uart->iir &= ~MICOUART_IIR_TXRDY; /* schedule an event to output the character. */ hw_event_queue_schedule (me, 1, do_uart_tx_event, 0); break; case LM32_UART_IER: uart->ier = value; if ((value & LM32_UART_IER_TX_INT) && (uart->lsr & LM32_UART_LSR_TX_RDY)) { /* hw_event_queue_schedule (me, 1, do_uart_tx_event, 0); */ uart->lsr |= LM32_UART_LSR_TX_RDY; uart->iir |= MICOUART_IIR_TXRDY; hw_port_event (me, INT_PORT, 1); } else if ((value & LM32_UART_IER_TX_INT) == 0) { hw_port_event (me, INT_PORT, 0); } break; case LM32_UART_IIR: uart->iir = value; break; case LM32_UART_LCR: uart->lcr = value; break; case LM32_UART_MCR: uart->mcr = value; break; case LM32_UART_LSR: uart->lsr = value; break; case LM32_UART_MSR: uart->msr = value; break; case LM32_UART_DIV: uart->div = value; break; default: hw_abort (me, "write to invalid register address: 0x%x.", uart_reg); } return nr_bytes; } static unsigned lm32uart_io_read_buffer (struct hw *me, void *dest, int space, unsigned_word base, unsigned nr_bytes) { struct lm32uart *uart = hw_data (me); int uart_reg; int value; unsigned char *dest_bytes = dest; fd_set fd; struct timeval tv; HW_TRACE ((me, "read 0x%08lx length %d", (long) base, (int) nr_bytes)); uart_reg = base - uart->base; switch (uart_reg) { case LM32_UART_RBR: value = getchar (); uart->lsr &= ~LM32_UART_LSR_RX_RDY; break; case LM32_UART_IER: value = uart->ier; break; case LM32_UART_IIR: value = uart->iir; break; case LM32_UART_LCR: value = uart->lcr; break; case LM32_UART_MCR: value = uart->mcr; break; case LM32_UART_LSR: /* Check to see if any data waiting in stdin. */ FD_ZERO (&fd); FD_SET (fileno (stdin), &fd); tv.tv_sec = 0; tv.tv_usec = 1; if (select (fileno (stdin) + 1, &fd, NULL, NULL, &tv)) uart->lsr |= LM32_UART_LSR_RX_RDY; value = uart->lsr; break; case LM32_UART_MSR: value = uart->msr; break; case LM32_UART_DIV: value = uart->div; break; default: hw_abort (me, "read from invalid register address: 0x%x.", uart_reg); } if (nr_bytes == 4) { dest_bytes[0] = value >> 24; dest_bytes[1] = value >> 16; dest_bytes[2] = value >> 8; dest_bytes[3] = value; } else hw_abort (me, "read of unsupported number of bytes: %d", nr_bytes); return nr_bytes; } static void attach_lm32uart_regs (struct hw *me, struct lm32uart *uart) { unsigned_word attach_address; int attach_space; unsigned attach_size; reg_property_spec reg; if (hw_find_property (me, "reg") == NULL) hw_abort (me, "Missing \"reg\" property"); if (!hw_find_reg_array_property (me, "reg", 0, ®)) hw_abort (me, "\"reg\" property must contain three addr/size entries"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); uart->base = attach_address; hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); uart->limit = attach_address + (attach_size - 1); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); } static void lm32uart_finish (struct hw *me) { struct lm32uart *uart; int i; uart = HW_ZALLOC (me, struct lm32uart); set_hw_data (me, uart); set_hw_io_read_buffer (me, lm32uart_io_read_buffer); set_hw_io_write_buffer (me, lm32uart_io_write_buffer); set_hw_ports (me, lm32uart_ports); /* Attach ourself to our parent bus. */ attach_lm32uart_regs (me, uart); /* Initialize the UART. */ uart->rbr = 0; uart->thr = 0; uart->ier = 0; uart->iir = 0; uart->lcr = 0; uart->mcr = 0; uart->lsr = LM32_UART_LSR_TX_RDY; uart->msr = 0; uart->div = 0; /* By setting to zero, characters are output immediately. */ } const struct hw_descriptor dv_lm32uart_descriptor[] = { {"lm32uart", lm32uart_finish,}, {NULL}, };