URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 1500 to Rev 1501
- ↔ Reverse comparison
Rev 1500 → Rev 1501
/trunk/or1ksim/peripheral/16450.c
52,6 → 52,12
|
#define MIN(a,b) ((a) < (b) ? (a) : (b)) |
|
void uart_recv_break(void *dat); |
void uart_recv_char(void *dat); |
void uart_check_vapi(void *dat); |
void uart_check_char(void *dat); |
static void uart_sched_recv_check(struct dev_16450 *uart); |
static void uart_vapi_cmd(void *dat); |
void uart_tx_send(void *dat); |
|
/* Number of clock cycles (one clock cycle is one call to the uart_clock()) |
220,6 → 226,119
} |
} |
|
/*-------------------------------------------------------[ Receiver logic ]---*/ |
/* Adds a character to the RX FIFO */ |
static void uart_add_char (struct dev_16450 *uart, int ch) |
{ |
uart->regs.lsr |= UART_LSR_RDRDY; |
uart->istat.timeout_count = 0; |
|
if (uart->istat.rxbuf_full + 1 > uart->fifo_len) { |
uart->regs.lsr |= UART_LSR_OVRRUN | UART_LSR_RXERR; |
} else { |
TRACE("add %02x\n", ch); |
uart->regs.rxbuf[uart->istat.rxbuf_head] = ch; |
uart->istat.rxbuf_head = (uart->istat.rxbuf_head + 1) % uart->fifo_len; |
if(!uart->istat.rxbuf_full++) { |
uart->regs.lsr |= ch >> 8; |
} |
} |
} |
|
/* Called when a break sequence is about to start. It stops receiveing |
* characters and schedules the uart_recv_break to send the break */ |
void uart_recv_break_start(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
uart->istat.receiveing = 0; |
uart->istat.recv_break = 1; |
|
SCHED_FIND_REMOVE(uart_recv_char, uart); |
|
if(uart->vapi_id && (uart->vapi_buf_head_ptr != uart->vapi_buf_tail_ptr)) |
uart_vapi_cmd(uart); |
|
SCHED_ADD(uart_recv_break, uart, |
UART_BREAK_COUNT * uart->vapi.char_clks * UART_CLOCK_DIVIDER); |
} |
|
/* Stops sending breaks and starts receiveing characters */ |
void uart_recv_break_stop(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
uart->istat.recv_break = 0; |
SCHED_FIND_REMOVE(uart_recv_break, dat); |
} |
|
/* Receives a break */ |
void uart_recv_break(void *dat) |
{ |
struct dev_16450 *uart = dat; |
unsigned lsr = UART_LSR_BREAK | UART_LSR_RXERR | UART_LSR_RDRDY; |
|
uart_add_char(uart, lsr << 8); |
} |
|
/* Moves a character from the serial register to the RX FIFO */ |
void uart_recv_char(void *dat) |
{ |
struct dev_16450 *uart = dat; |
uint16_t char_to_add; |
|
/* Set unused character bits to zero and allow lsr register in fifo */ |
char_to_add = uart->iregs.rxser & (((1 << ((uart->regs.lcr & 3) + 5)) - 1) | 0xff00); |
|
TRACE("Receiving 0x%02"PRIx16"'%c' via UART at %"PRIxADDR"\n", |
char_to_add, (char)char_to_add, uart->baseaddr); |
PRINTF ("%c", (char)char_to_add); |
|
if (uart->regs.mcr & UART_MCR_LOOP) { |
uart->iregs.rxser = uart->iregs.loopback; |
uart->istat.receiveing = 1; |
SCHED_ADD(uart_recv_char, uart, uart->char_clks * UART_CLOCK_DIVIDER); |
} else { |
uart->istat.receiveing = 0; |
uart_sched_recv_check(uart); |
if(uart->vapi_id && (uart->vapi_buf_head_ptr != uart->vapi_buf_tail_ptr)) |
SCHED_ADD(uart_vapi_cmd, uart, 0); |
} |
|
uart_add_char(uart, char_to_add); |
} |
|
/* Checks if there is a character waiting to be received */ |
void uart_check_char(void *dat) |
{ |
struct dev_16450 *uart = dat; |
char buffer[1]; |
int retval; |
|
/* Check if there is something waiting, and put it into rxser */ |
retval = channel_read(uart->channel, buffer, 1); |
if(retval > 0) { |
uart->iregs.rxser = (unsigned char)buffer[0]; |
uart->istat.receiveing = 1; |
SCHED_ADD(uart_recv_char, uart, uart->char_clks * UART_CLOCK_DIVIDER); |
return; |
} |
|
if(!retval) { |
SCHED_ADD(uart_check_char, uart, UART_FGETC_SLOWDOWN * UART_CLOCK_DIVIDER); |
return; |
} |
|
if(retval < 0) |
perror(uart->channel_str); |
} |
|
static void uart_sched_recv_check(struct dev_16450 *uart) |
{ |
if(!uart->vapi_id) |
SCHED_ADD(uart_check_char, uart, UART_FGETC_SLOWDOWN * UART_CLOCK_DIVIDER); |
} |
|
/* Set a specific UART register with value. */ |
void uart_write_byte(oraddr_t addr, uint8_t value, void *dat) |
{ |
343,10 → 462,12
value = uart->regs.rxbuf[uart->istat.rxbuf_tail]; |
uart->istat.rxbuf_tail = (uart->istat.rxbuf_tail + 1) % uart->fifo_len; |
uart->istat.rxbuf_full--; |
} |
TRACE("Reading %"PRIx8" out of RX FIFO\n", value); |
} else |
TRACE("Trying to read out of RX FIFO but it's empty!\n"); |
|
if (uart->istat.rxbuf_full) |
uart->regs.lsr |= UART_LSR_RDRDY; |
if(uart->istat.rxbuf_full) |
uart->regs.lsr |= UART_LSR_RDRDY | uart->regs.rxbuf[uart->istat.rxbuf_tail] >> 8; |
else |
uart->regs.lsr &= ~UART_LSR_RDRDY; |
|
388,8 → 509,9
|
/*--------------------------------------------------------[ VAPI handling ]---*/ |
/* Decodes the read vapi command */ |
static void uart_vapi_cmd(struct dev_16450 *uart) |
static void uart_vapi_cmd(void *dat) |
{ |
struct dev_16450 *uart = dat; |
int received = 0; |
|
while (!received) { |
425,7 → 547,10
if(uart->regs.lcr & UART_LCR_PARITY) |
uart->iregs.rxser |= UART_LSR_PARITY << 8; |
} |
uart->istat.rxser_full = 1; |
if(!uart->istat.recv_break) { |
uart->istat.receiveing = 1; |
SCHED_ADD(uart_recv_char, uart, uart->char_clks * UART_CLOCK_DIVIDER); |
} |
received = 1; |
break; |
case 0x01: |
439,8 → 564,25
uart->vapi.skew = (signed short)(data & 0xffff); |
break; |
case 0x04: |
uart->vapi.next_break_cnt = data & 0xffff; |
uart->vapi.next_break = (data >> 16) & 1; |
if((data >> 16) & 1) { |
/* If data & 0xffff is 0 then set the break imediatly and handle the |
* following commands as appropriate */ |
if(!(data & 0xffff)) |
uart_recv_break_start(uart); |
else |
/* Schedule a job to start sending breaks */ |
SCHED_ADD(uart_recv_break_start, uart, |
(data & 0xffff) * UART_CLOCK_DIVIDER); |
} else { |
/* If data & 0xffff is 0 then release the break imediatly and handle |
* the following commands as appropriate */ |
if(!(data & 0xffff)) |
uart_recv_break_stop(uart); |
else |
/* Schedule a job to stop sending breaks */ |
SCHED_ADD(uart_recv_break_stop, uart, |
(data & 0xffff) * UART_CLOCK_DIVIDER); |
} |
break; |
default: |
WARN("WARNING: Invalid vapi command %02lx\n", data >> 24); |
461,30 → 603,15
fprintf (stderr, "FATAL: uart VAPI buffer to small.\n"); |
exit (1); |
} |
if(!uart->istat.receiveing) |
uart_vapi_cmd(uart); |
} |
|
/* Adds a character to the FIFO */ |
|
void uart_add_char (struct dev_16450 *uart, int ch) |
{ |
if (uart->istat.rxbuf_full + 1 > uart->fifo_len) |
uart->regs.lsr |= UART_LSR_OVRRUN | UART_LSR_RXERR; |
else { |
TRACE("add %02x\n", ch); |
uart->regs.rxbuf[uart->istat.rxbuf_head] = ch; |
uart->istat.rxbuf_head = (uart->istat.rxbuf_head + 1) % uart->fifo_len; |
uart->istat.rxbuf_full++; |
} |
uart->regs.lsr |= UART_LSR_RDRDY; |
uart->istat.timeout_count = 0; |
} |
|
/* Simulation hook. Must be called every clock cycle to simulate all UART |
devices. It does internal functional UART simulation. */ |
void uart_clock16 (void *dat) |
{ |
struct dev_16450 *uart = dat; |
int retval; |
|
/* Schedule for later */ |
SCHED_ADD (uart_clock16, dat, UART_CLOCK_DIVIDER); |
498,76 → 625,6
|
TRACE("\tChannel stream or VAPI checks out ok\n"); |
|
if (uart->vapi.next_break_cnt >= 0) |
if (--uart->vapi.next_break_cnt < 0) { |
if (!(uart->vapi.cur_break = uart->vapi.next_break)) |
uart->istat.break_set = 0; |
} |
|
/***************** Receive *****************/ |
|
/* Is there a break? */ |
if (uart->vapi.cur_break) { |
uart->vapi.cur_break_cnt++; |
if (uart->vapi.cur_break_cnt > UART_BREAK_COUNT * uart->vapi.char_clks) { |
if (!uart->istat.break_set) { |
unsigned lsr; |
uart->istat.break_set = 1; |
lsr = UART_LSR_BREAK | UART_LSR_RXERR | UART_LSR_RDRDY; |
PRINTF ("[%x]\n", uart->regs.lsr); |
uart->istat.rxser_full = 0; |
uart->istat.rxser_clks = 0; |
uart_add_char (uart, lsr << 8); |
} else |
uart->vapi.cur_break_cnt = 0; |
} |
if (uart->istat.rxser_full) { |
uart->istat.rxser_full = 0; |
uart->istat.rxser_clks = 0; |
} |
} else { |
if (uart->istat.rxser_full) { |
if (uart->char_clks <= uart->istat.rxser_clks++) { |
/* Set unused character bits to zero and allow lsr register in fifo */ |
uart->iregs.rxser &= ((1 << ((uart->regs.lcr & 3) + 5)) - 1) | 0xff00; |
TRACE("\tReceiving 0x%02lx'%c' via UART (at %"PRIxADDR"...\n", |
uart->iregs.rxser, (char)uart->iregs.rxser, uart->baseaddr); |
PRINTF ("%c", (char)uart->iregs.rxser); |
uart->istat.rxser_full = 0; |
uart->istat.rxser_clks = 0; |
uart_add_char (uart, uart->iregs.rxser); |
} |
} |
} |
|
/* Check if there is something waiting, and put it into rxser */ |
if (uart->regs.mcr & UART_MCR_LOOP) { |
uart->iregs.rxser = uart->iregs.loopback; |
uart->istat.rxser_full = 1; |
} else { |
if (!uart->vapi_id) { |
if(uart->istat.rxser_full == 0) { |
if (uart->slowdown) |
uart->slowdown--; |
else { |
char buffer[1]; |
retval = channel_read(uart->channel, buffer, 1); |
if(retval < 0) |
perror(uart->channel_str); |
else if(retval > 0) { |
uart->iregs.rxser = (unsigned char)buffer[0]; |
uart->istat.rxser_full = 1; |
} else |
uart->slowdown = UART_FGETC_SLOWDOWN; |
} |
} |
} else { /* VAPI */ |
/* do not handle commands while receiving */ |
if (uart->istat.rxser_full) return; |
uart_vapi_cmd(uart); |
} |
} |
|
/***************** Loopback *****************/ |
if (uart->regs.mcr & UART_MCR_LOOP) { |
TRACE("uart_clock: Loopback\n"); |
594,13 → 651,6
if (uart->regs.lsr & UART_LSR_RDRDY) |
uart->istat.timeout_count++; |
|
/* Update LSR error bits from the ones from rx FIFO */ |
if (uart->istat.rxbuf_full) { |
uart->regs.lsr |= uart->regs.rxbuf[uart->istat.rxbuf_tail] >> 8; |
/* we must delete the lsr status, so that we can clear it from lsr */ |
uart->regs.rxbuf[uart->istat.rxbuf_tail] &= 0xff; |
} |
|
/* Interrupt detection in proper priority order. */ |
uart->regs.iir = UART_IIR_NO_INT; |
if (uart->regs.ier & UART_IER_RLSI && /* Receiver LS */ |
660,13 → 710,9
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0; |
uart->istat.txbuf_head = uart->istat.txbuf_tail = 0; |
|
uart->istat.rxser_full = 0; |
uart->istat.txbuf_full = uart->istat.rxbuf_full = 0; |
|
uart->istat.rxser_clks = 0; |
|
uart->istat.thre_int = 0; |
uart->istat.break_set = 0; |
uart->istat.timeout_count = 0; |
|
// For FIFO-mode only, THRE interrupt is set when both THR and FIFO are empty |
673,11 → 719,12
uart->istat.thre_int = (uart->fifo_len == 16); |
|
uart->char_clks = 0; |
uart->slowdown = UART_FGETC_SLOWDOWN; |
|
uart->iregs.txser = 0; |
uart->iregs.rxser = 0; |
uart->iregs.loopback = 0; |
uart->istat.receiveing = 0; |
uart->istat.recv_break = 0; |
|
memset(uart->regs.txbuf, 0, sizeof(uart->regs.txbuf)); |
memset(uart->regs.rxbuf, 0, sizeof(uart->regs.rxbuf)); |
693,8 → 740,6
uart->regs.msr = 0; |
uart->regs.scr = 0; |
|
uart->vapi.cur_break = uart->vapi.cur_break_cnt = uart->vapi.next_break = 0; |
uart->vapi.next_break_cnt = -1; |
uart->vapi.skew = 0; |
uart->vapi.lcr = 0; |
uart->vapi.dll = 0; |
705,6 → 750,7
memset(uart->vapi_buf, 0, sizeof(uart->vapi_buf)); |
|
SCHED_ADD (uart_clock16, dat, UART_CLOCK_DIVIDER); |
uart_sched_recv_check(uart); |
} |
|
/* Print register values on stdout. */ |
732,8 → 778,6
|
PRINTF("\nInternal status (sim debug):\n"); |
PRINTF("char_clks: %ld\n", uart->char_clks); |
PRINTF("rxser_clks: %ld\n", uart->istat.rxser_clks); |
PRINTF("rxser: %d\n", uart->istat.rxser_full); |
PRINTF("rxbuf_full: %d txbuf_full: %d\n", uart->istat.rxbuf_full, uart->istat.txbuf_full); |
PRINTF("Using IRQ%i\n", uart->irq); |
if (uart->vapi_id) |
/trunk/or1ksim/peripheral/16450.h
57,13 → 57,12
int txbuf_tail; |
int rxbuf_head; |
int rxbuf_tail; |
unsigned int rxser_full; |
unsigned int txbuf_full; |
unsigned int rxbuf_full; |
unsigned thre_int; |
unsigned break_set; |
unsigned long rxser_clks; |
unsigned timeout_count; |
int receiveing; /* Receiveing a char */ |
int recv_break; /* Receiveing a break */ |
} istat; /* Internal status */ |
|
/* Clocks per char */ |
75,10 → 74,6
int dll, dlh; |
int lcr; |
int skew; |
int next_break; |
int next_break_cnt; |
int cur_break; |
int cur_break_cnt; |
} vapi; |
|
/* Required by VAPI - circular buffer */ |
91,9 → 86,6
/* Length of FIFO, 16 for 16550, 1 for 16450 */ |
int fifo_len; |
|
/* fgetc slowdown */ |
int slowdown; |
|
struct channel *channel; |
|
/* Configuration */ |