interrupt-driven uart output, hopefully a nice example for teaching.
This commit is contained in:
parent
1e72d5ca08
commit
27057bc9b4
|
@ -27,6 +27,8 @@
|
||||||
|
|
||||||
//
|
//
|
||||||
// send one character to the uart.
|
// send one character to the uart.
|
||||||
|
// called by printf, and to echo input characters,
|
||||||
|
// but not from write().
|
||||||
//
|
//
|
||||||
void
|
void
|
||||||
consputc(int c)
|
consputc(int c)
|
||||||
|
@ -40,9 +42,9 @@ consputc(int c)
|
||||||
|
|
||||||
if(c == BACKSPACE){
|
if(c == BACKSPACE){
|
||||||
// if the user typed backspace, overwrite with a space.
|
// if the user typed backspace, overwrite with a space.
|
||||||
uartputc('\b'); uartputc(' '); uartputc('\b');
|
uartputc('\b', 0); uartputc(' ', 0); uartputc('\b', 0);
|
||||||
} else {
|
} else {
|
||||||
uartputc(c);
|
uartputc(c, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +72,7 @@ consolewrite(int user_src, uint64 src, int n)
|
||||||
char c;
|
char c;
|
||||||
if(either_copyin(&c, user_src, src+i, 1) == -1)
|
if(either_copyin(&c, user_src, src+i, 1) == -1)
|
||||||
break;
|
break;
|
||||||
consputc(c);
|
uartputc(c, 1);
|
||||||
}
|
}
|
||||||
release(&cons.lock);
|
release(&cons.lock);
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ void usertrapret(void);
|
||||||
// uart.c
|
// uart.c
|
||||||
void uartinit(void);
|
void uartinit(void);
|
||||||
void uartintr(void);
|
void uartintr(void);
|
||||||
void uartputc(int);
|
void uartputc(int, int);
|
||||||
int uartgetc(void);
|
int uartgetc(void);
|
||||||
|
|
||||||
// vm.c
|
// vm.c
|
||||||
|
|
|
@ -33,7 +33,6 @@ int
|
||||||
plic_claim(void)
|
plic_claim(void)
|
||||||
{
|
{
|
||||||
int hart = cpuid();
|
int hart = cpuid();
|
||||||
//int irq = *(uint32*)(PLIC + 0x201004);
|
|
||||||
int irq = *(uint32*)PLIC_SCLAIM(hart);
|
int irq = *(uint32*)PLIC_SCLAIM(hart);
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +42,5 @@ void
|
||||||
plic_complete(int irq)
|
plic_complete(int irq)
|
||||||
{
|
{
|
||||||
int hart = cpuid();
|
int hart = cpuid();
|
||||||
//*(uint32*)(PLIC + 0x201004) = irq;
|
|
||||||
*(uint32*)PLIC_SCLAIM(hart) = irq;
|
*(uint32*)PLIC_SCLAIM(hart) = irq;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,8 +91,9 @@ usertrapret(void)
|
||||||
{
|
{
|
||||||
struct proc *p = myproc();
|
struct proc *p = myproc();
|
||||||
|
|
||||||
// turn off interrupts, since we're switching
|
// we're about to switch the destination of traps from
|
||||||
// now from kerneltrap() to usertrap().
|
// kerneltrap() to usertrap(), so turn off interrupts until
|
||||||
|
// we're back in user space, where usertrap() is correct.
|
||||||
intr_off();
|
intr_off();
|
||||||
|
|
||||||
// send syscalls, interrupts, and exceptions to trampoline.S
|
// send syscalls, interrupts, and exceptions to trampoline.S
|
||||||
|
@ -192,6 +193,9 @@ devintr()
|
||||||
printf("unexpected interrupt irq=%d\n", irq);
|
printf("unexpected interrupt irq=%d\n", irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the PLIC allows each device to raise at most one
|
||||||
|
// interrupt at a time; tell the PLIC the device is
|
||||||
|
// now allowed to interrupt again.
|
||||||
if(irq)
|
if(irq)
|
||||||
plic_complete(irq);
|
plic_complete(irq);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
// the UART control registers.
|
// the UART control registers.
|
||||||
// some have different meanings for
|
// some have different meanings for
|
||||||
// read vs write.
|
// read vs write.
|
||||||
// http://byterunner.com/16550.html
|
// see http://byterunner.com/16550.html
|
||||||
#define RHR 0 // receive holding register (for input bytes)
|
#define RHR 0 // receive holding register (for input bytes)
|
||||||
#define THR 0 // transmit holding register (for output bytes)
|
#define THR 0 // transmit holding register (for output bytes)
|
||||||
#define IER 1 // interrupt enable register
|
#define IER 1 // interrupt enable register
|
||||||
|
@ -30,6 +30,15 @@
|
||||||
#define ReadReg(reg) (*(Reg(reg)))
|
#define ReadReg(reg) (*(Reg(reg)))
|
||||||
#define WriteReg(reg, v) (*(Reg(reg)) = (v))
|
#define WriteReg(reg, v) (*(Reg(reg)) = (v))
|
||||||
|
|
||||||
|
// the transmit output buffer.
|
||||||
|
struct spinlock uart_tx_lock;
|
||||||
|
#define UART_TX_BUF_SIZE 32
|
||||||
|
char uart_tx_buf[UART_TX_BUF_SIZE];
|
||||||
|
int uart_tx_w; // write next to uart_tx_buf[uart_tx_w++]
|
||||||
|
int uart_tx_r; // read next from uart_tx_buf[uar_tx_r++]
|
||||||
|
|
||||||
|
void uartstart();
|
||||||
|
|
||||||
void
|
void
|
||||||
uartinit(void)
|
uartinit(void)
|
||||||
{
|
{
|
||||||
|
@ -52,18 +61,79 @@ uartinit(void)
|
||||||
// reset and enable FIFOs.
|
// reset and enable FIFOs.
|
||||||
WriteReg(FCR, 0x07);
|
WriteReg(FCR, 0x07);
|
||||||
|
|
||||||
// enable receive interrupts.
|
// enable transmit and receive interrupts.
|
||||||
WriteReg(IER, 0x01);
|
WriteReg(IER, 0x02 | 0x01);
|
||||||
|
|
||||||
|
initlock(&uart_tx_lock, "uart");
|
||||||
}
|
}
|
||||||
|
|
||||||
// write one output character to the UART.
|
// add a character to the output buffer and tell the
|
||||||
|
// UART to start sending if it isn't already.
|
||||||
|
//
|
||||||
|
// usually called from the top-half -- by a process
|
||||||
|
// calling write(). can also be called from a uart
|
||||||
|
// interrupt to echo a received character, or by printf
|
||||||
|
// or panic from anywhere in the kernel.
|
||||||
|
//
|
||||||
|
// the block argument controls what happens if the
|
||||||
|
// buffer is full. for write(), block is 1, and the
|
||||||
|
// process waits. for kernel printf's and echoed
|
||||||
|
// characters, block is 0, and the character is
|
||||||
|
// discarded; this is necessary since sleep() is
|
||||||
|
// not possible in interrupts.
|
||||||
void
|
void
|
||||||
uartputc(int c)
|
uartputc(int c, int block)
|
||||||
{
|
{
|
||||||
// wait for Transmit Holding Empty to be set in LSR.
|
acquire(&uart_tx_lock);
|
||||||
while((ReadReg(LSR) & (1 << 5)) == 0)
|
while(1){
|
||||||
;
|
if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){
|
||||||
WriteReg(THR, c);
|
// buffer is full.
|
||||||
|
if(block){
|
||||||
|
// wait for uartstart() to open up space in the buffer.
|
||||||
|
sleep(&uart_tx_r, &uart_tx_lock);
|
||||||
|
} else {
|
||||||
|
// caller does not want us to wait.
|
||||||
|
release(&uart_tx_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uart_tx_buf[uart_tx_w] = c;
|
||||||
|
uart_tx_w = (uart_tx_w + 1) % UART_TX_BUF_SIZE;
|
||||||
|
uartstart();
|
||||||
|
release(&uart_tx_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the UART is idle, and a character is waiting
|
||||||
|
// in the transmit buffer, send it.
|
||||||
|
// caller must hold uart_tx_lock.
|
||||||
|
// called from both the top- and bottom-half.
|
||||||
|
void
|
||||||
|
uartstart()
|
||||||
|
{
|
||||||
|
while(1){
|
||||||
|
if(uart_tx_w == uart_tx_r){
|
||||||
|
// transmit buffer is empty.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((ReadReg(LSR) & (1 << 5)) == 0){
|
||||||
|
// the UART transmit holding register is full,
|
||||||
|
// so we cannot give it another byte.
|
||||||
|
// it will interrupt when it's ready for a new byte.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int c = uart_tx_buf[uart_tx_r];
|
||||||
|
uart_tx_r = (uart_tx_r + 1) % UART_TX_BUF_SIZE;
|
||||||
|
|
||||||
|
// maybe uartputc() is waiting for space in the buffer.
|
||||||
|
wakeup(&uart_tx_r);
|
||||||
|
|
||||||
|
WriteReg(THR, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read one input character from the UART.
|
// read one input character from the UART.
|
||||||
|
@ -79,14 +149,22 @@ uartgetc(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trap.c calls here when the uart interrupts.
|
// handle a uart interrupt, raised because input has
|
||||||
|
// arrived, or the uart is ready for more output, or
|
||||||
|
// both. called from trap.c.
|
||||||
void
|
void
|
||||||
uartintr(void)
|
uartintr(void)
|
||||||
{
|
{
|
||||||
|
// read and process incoming characters.
|
||||||
while(1){
|
while(1){
|
||||||
int c = uartgetc();
|
int c = uartgetc();
|
||||||
if(c == -1)
|
if(c == -1)
|
||||||
break;
|
break;
|
||||||
consoleintr(c);
|
consoleintr(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send buffered characters.
|
||||||
|
acquire(&uart_tx_lock);
|
||||||
|
uartstart();
|
||||||
|
release(&uart_tx_lock);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue