-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite reading from UART to be interrupt-driven
* Rewrite UART code into a driver * Introduce platform-level interrupt (PLIC) handling, which UART interrupts come in through * Handle incoming characters in the uart driver and accumulate them in a buffer * When reading from stdin, try reading from the uart driver's buffer and put the process to sleep if the buffer has less than at least one full line of data (or is full) * When the driver sees an incoming newline (or fills the buffer to the brim), wake up the process waiting on the driver
- Loading branch information
Showing
19 changed files
with
346 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#ifndef _DRIVERS_H_ | ||
#define _DRIVERS_H_ | ||
|
||
void drivers_init(); | ||
|
||
#endif // ifndef _DRIVERS_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#ifndef _UART_H_ | ||
#define _UART_H_ | ||
|
||
#include "fs.h" | ||
|
||
#ifndef UART_BASE | ||
#define UART_BASE 0x10010000 | ||
#endif | ||
|
||
#define UART_TXDATA 0x00 | ||
#define UART_RXDATA 0x04 | ||
#define UART_TXCTRL 0x08 | ||
#define UART_RXCTRL 0x0c | ||
#define UART_IE 0x10 | ||
#define UART_IP 0x14 | ||
#define UART_BAUD_RATE_DIVISOR 0x18 | ||
|
||
#define UART_IE_TXWM 1 | ||
#define UART_IE_RXWM 2 | ||
|
||
#define ASCII_DEL 0x7f | ||
|
||
#define UART_BUF_SZ 64 | ||
|
||
typedef struct uart_state_s { | ||
// TODO: spinlock lock; | ||
char rxbuf[UART_BUF_SZ]; | ||
char txbuf[UART_BUF_SZ]; | ||
int rx_rpos; | ||
int rx_wpos; | ||
int tx_rpos; | ||
int tx_wpos; | ||
int rx_num_newlines; // number of '\n' chars in the buffer | ||
int rx_buff_full; // indicates that rxbuf got full | ||
} uart_state_t; | ||
|
||
extern uart_state_t uart0; // defined in uart.c | ||
|
||
void uart_init(); | ||
|
||
// uart_enqueue_chars reads characters from rx FIFO and enqueues them into the | ||
// uart0's rxbuf. If rxbuf is full, rx_buff_full is set to 1 and all | ||
// incoming data is dropped on the floor (unless it's a backspace, which frees | ||
// up some space instead of using up more buffer space). | ||
// | ||
// When a newline character gets enqueued, all processes waiting on uart0 are | ||
// woken up to get them a chance to continue reading. | ||
int uart_enqueue_chars(); | ||
|
||
// uart_writechar writes a single char to UART synchronously. | ||
void uart_writechar(char ch); | ||
|
||
// uart_readline attempts to read a full line of characters from rxbuf. If | ||
// rxbuf is empty or does not contain a newline character, the calling process | ||
// yields execution and goes to sleep. | ||
int32_t uart_readline(char* buf, uint32_t bufsize); | ||
|
||
int32_t uart_print(char const* data, uint32_t size); | ||
|
||
int32_t uart_read(file_t* f, uint32_t pos, void* buf, uint32_t bufsize); | ||
int32_t uart_write(file_t* f, uint32_t pos, void* buf, uint32_t bufsize); | ||
|
||
// implemented in uart-print.s | ||
extern int32_t uart_prints(char const* data); | ||
extern int32_t uart_printc(char c); | ||
|
||
#endif // ifndef _UART_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#ifndef _PLIC_H_ | ||
#define _PLIC_H_ | ||
|
||
#define PLIC_BASE 0x0c000000 | ||
#define PLIC_PRIORITY (PLIC_BASE + 0x00000000) | ||
#define PLIC_PENDING (PLIC_BASE + 0x00001000) | ||
#define PLIC_ENABLE (PLIC_BASE + 0x00002000) | ||
#define PLIC_THRESHOLD (PLIC_BASE + 0x00200000) | ||
#define PLIC_CLAIM_RW (PLIC_BASE + 0x00200004) | ||
|
||
#define PLIC_MAX_PRIORITY 7 | ||
|
||
// plic_enable_intr enables a specified interrupt. | ||
void plic_enable_intr(int intr_no); | ||
|
||
// plic_set_intr_priority sets a priority on a given interrupt. 7 is the | ||
// highest priority, while a zero priority effectively disables an interrupt. | ||
void plic_set_intr_priority(int intr_no, int priority); | ||
|
||
// plic_set_threshold sets a global threshold for PLIC interrupts. Only the | ||
// interrupts with priority>threshold will be triggered. | ||
void plic_set_threshold(int threshold); | ||
|
||
// plic_dispatch_interrupts does the actual job of handling a PLIC interrupt | ||
// and calling the respective subsystems. | ||
void plic_dispatch_interrupts(); | ||
|
||
#endif // ifndef _PLIC_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#include "drivers/uart/uart.h" | ||
|
||
void drivers_init() { | ||
uart_init(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
#include "sys.h" | ||
#include "proc.h" | ||
#include "drivers/uart/uart.h" | ||
#include "plic.h" | ||
|
||
uart_state_t uart0; | ||
|
||
void uart_init() { | ||
uart0.rx_rpos = 0; | ||
uart0.rx_wpos = 0; | ||
uart0.tx_rpos = 0; | ||
uart0.tx_wpos = 0; | ||
uart0.rx_num_newlines = 0; | ||
uart0.rx_buff_full = 0; | ||
|
||
// enable reading: | ||
*(uint32_t*)(UART_BASE + UART_RXCTRL) = 1; | ||
|
||
// enable read interrupts: | ||
*(uint32_t*)(UART_BASE + UART_IE) = UART_IE_RXWM; | ||
|
||
// set baud divisor (the SiFive FE310-G002 manual lists a table of possible | ||
// values in Section 18.9, determined this particular choice | ||
// experimentally. Furthermore, it's the default on HiFive1-revB board): | ||
*(uint32_t*)(UART_BASE + UART_BAUD_RATE_DIVISOR) = 138; | ||
|
||
plic_enable_intr(UART0_IRQ_NO); | ||
plic_set_intr_priority(UART0_IRQ_NO, PLIC_MAX_PRIORITY); | ||
plic_set_threshold(PLIC_MAX_PRIORITY - 1); | ||
} | ||
|
||
// _decrement_wpos attempts to decrement a given wpos. It doesn't decrement | ||
// behind rx_rpos and doesn't decrement onto the last newline. It also wraps | ||
// around zero. | ||
int _decrement_wpos(int wpos) { | ||
int orig_wpos = wpos; | ||
if (wpos == uart0.rx_rpos) { | ||
return orig_wpos; | ||
} | ||
wpos--; | ||
if (wpos < 0) { | ||
wpos = UART_BUF_SZ - 1; | ||
} | ||
if (uart0.rxbuf[wpos] == '\n') { | ||
return orig_wpos; | ||
} | ||
return wpos; | ||
} | ||
|
||
int uart_enqueue_chars() { | ||
volatile int32_t* rx = (int32_t*)(UART_BASE + UART_RXDATA); | ||
int wpos = uart0.rx_wpos; | ||
int num_enqueued = 0; | ||
while (1) { | ||
int32_t word = *rx; | ||
if (word < 0) { | ||
break; | ||
} | ||
char ch = word & 0xff; | ||
if (ch == ASCII_DEL) { | ||
int new_wpos = _decrement_wpos(wpos); | ||
if (new_wpos != wpos) { | ||
wpos = new_wpos; | ||
uart_writechar('\b'); | ||
uart_writechar(' '); | ||
uart_writechar('\b'); | ||
// we've decremented wpos, so unmark the buffer as full if it was such | ||
if (uart0.rx_buff_full) { | ||
uart0.rx_buff_full = 0; | ||
} | ||
} | ||
continue; | ||
} | ||
if (uart0.rx_buff_full) { | ||
proc_mark_for_wakeup(&uart0); | ||
continue; // if rxbuf is full, just keep draining rx FIFO | ||
} | ||
if (ch == '\r') { | ||
ch = '\n'; | ||
uart0.rx_num_newlines++; | ||
proc_mark_for_wakeup(&uart0); | ||
} | ||
uart_writechar(ch); // echo back to console | ||
uart0.rxbuf[wpos] = ch; | ||
wpos++; | ||
num_enqueued++; | ||
if (wpos >= UART_BUF_SZ) { | ||
wpos = 0; | ||
} | ||
if (wpos == uart0.rx_rpos) { | ||
uart0.rx_buff_full = 1; | ||
} | ||
} | ||
uart0.rx_wpos = wpos; | ||
return num_enqueued; | ||
} | ||
|
||
void uart_writechar(char ch) { | ||
volatile int32_t* tx = (int32_t*)(UART_BASE + UART_TXDATA); | ||
while ((int32_t)(*tx) < 0); | ||
*tx = ch; | ||
} | ||
|
||
// _can_read_from checks whether the internal state of a given uart device | ||
// allows to read from it. | ||
int _can_read_from(uart_state_t *uart) { | ||
if (uart->rx_num_newlines > 0) { | ||
return 1; | ||
} | ||
if (uart->rx_buff_full == 1) { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
|
||
int32_t uart_readline(char* buf, uint32_t bufsize) { | ||
while (!_can_read_from(&uart0)) { | ||
proc_yield(&uart0); | ||
} | ||
int32_t nread = 0; | ||
int32_t rpos = uart0.rx_rpos; | ||
int32_t wpos = uart0.rx_wpos; | ||
for (;;) { | ||
if (nread >= bufsize) { | ||
break; | ||
} | ||
if (rpos == wpos && nread > 0) { | ||
break; | ||
} | ||
char ch = uart0.rxbuf[rpos]; | ||
rpos++; | ||
if (rpos == UART_BUF_SZ) { | ||
rpos = 0; | ||
} | ||
if (ch == '\n') { | ||
uart0.rx_num_newlines--; | ||
break; | ||
} | ||
buf[nread] = ch; | ||
nread++; | ||
} | ||
if (uart0.rx_rpos != rpos || nread > 0) { | ||
uart0.rx_buff_full = 0; // at least one char was read, so it's no longer full | ||
} | ||
uart0.rx_rpos = rpos; | ||
return nread; | ||
} | ||
|
||
int32_t uart_print(char const* data, uint32_t size) { | ||
if (size == -1) { | ||
return uart_prints(data); | ||
} | ||
int i = 0; | ||
while (i < size) { | ||
uart_printc(data[i]); | ||
i++; | ||
} | ||
return i; | ||
} | ||
|
||
int32_t uart_read(file_t* f, uint32_t pos, void* buf, uint32_t bufsize) { | ||
return uart_readline(buf, bufsize); | ||
} | ||
|
||
int32_t uart_write(file_t* f, uint32_t pos, void* buf, uint32_t bufsize) { | ||
return uart_print(buf, bufsize); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.