diff options
| author | Robert Morris <rtm@csail.mit.edu> | 2020-07-20 06:59:26 -0400 | 
|---|---|---|
| committer | Robert Morris <rtm@csail.mit.edu> | 2020-07-20 06:59:26 -0400 | 
| commit | 823864099d0d8def9d4bcf9e382cf42e05bd7afa (patch) | |
| tree | 51f344b42031f0a5a41fcf08d00694bb13f10997 /kernel | |
| parent | 3b053f5d58b4914c6389588ad4e556bc887bc99d (diff) | |
| download | xv6-labs-823864099d0d8def9d4bcf9e382cf42e05bd7afa.tar.gz xv6-labs-823864099d0d8def9d4bcf9e382cf42e05bd7afa.tar.bz2 xv6-labs-823864099d0d8def9d4bcf9e382cf42e05bd7afa.zip | |
interrupt-driven uart output, hopefully a nice example for teaching.
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/console.c | 8 | ||||
| -rw-r--r-- | kernel/defs.h | 2 | ||||
| -rw-r--r-- | kernel/plic.c | 2 | ||||
| -rw-r--r-- | kernel/trap.c | 8 | ||||
| -rw-r--r-- | kernel/uart.c | 98 | 
5 files changed, 100 insertions, 18 deletions
| diff --git a/kernel/console.c b/kernel/console.c index 87a83ff..a97ef30 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -27,6 +27,8 @@  //  // send one character to the uart. +// called by printf, and to echo input characters, +// but not from write().  //  void  consputc(int c) @@ -40,9 +42,9 @@ consputc(int c)    if(c == BACKSPACE){      // if the user typed backspace, overwrite with a space. -    uartputc('\b'); uartputc(' '); uartputc('\b'); +    uartputc('\b', 0); uartputc(' ', 0); uartputc('\b', 0);    } else { -    uartputc(c); +    uartputc(c, 0);    }  } @@ -70,7 +72,7 @@ consolewrite(int user_src, uint64 src, int n)      char c;      if(either_copyin(&c, user_src, src+i, 1) == -1)        break; -    consputc(c); +    uartputc(c, 1);    }    release(&cons.lock); diff --git a/kernel/defs.h b/kernel/defs.h index f33f1f6..1b164a4 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -149,7 +149,7 @@ void            usertrapret(void);  // uart.c  void            uartinit(void);  void            uartintr(void); -void            uartputc(int); +void            uartputc(int, int);  int             uartgetc(void);  // vm.c diff --git a/kernel/plic.c b/kernel/plic.c index eed8316..5acba39 100644 --- a/kernel/plic.c +++ b/kernel/plic.c @@ -33,7 +33,6 @@ int  plic_claim(void)  {    int hart = cpuid(); -  //int irq = *(uint32*)(PLIC + 0x201004);    int irq = *(uint32*)PLIC_SCLAIM(hart);    return irq;  } @@ -43,6 +42,5 @@ void  plic_complete(int irq)  {    int hart = cpuid(); -  //*(uint32*)(PLIC + 0x201004) = irq;    *(uint32*)PLIC_SCLAIM(hart) = irq;  } diff --git a/kernel/trap.c b/kernel/trap.c index 77ff868..a63249e 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -91,8 +91,9 @@ usertrapret(void)  {    struct proc *p = myproc(); -  // turn off interrupts, since we're switching -  // now from kerneltrap() to usertrap(). +  // we're about to switch the destination of traps from +  // kerneltrap() to usertrap(), so turn off interrupts until +  // we're back in user space, where usertrap() is correct.    intr_off();    // send syscalls, interrupts, and exceptions to trampoline.S @@ -192,6 +193,9 @@ devintr()        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)        plic_complete(irq); diff --git a/kernel/uart.c b/kernel/uart.c index 3a5cdc4..4d70b42 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -18,7 +18,7 @@  // the UART control registers.  // some have different meanings for  // read vs write. -// http://byterunner.com/16550.html +// see http://byterunner.com/16550.html  #define RHR 0 // receive holding register (for input bytes)  #define THR 0 // transmit holding register (for output bytes)  #define IER 1 // interrupt enable register @@ -30,6 +30,15 @@  #define ReadReg(reg) (*(Reg(reg)))  #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  uartinit(void)  { @@ -52,18 +61,79 @@ uartinit(void)    // reset and enable FIFOs.    WriteReg(FCR, 0x07); -  // enable receive interrupts. -  WriteReg(IER, 0x01); +  // enable transmit and receive interrupts. +  WriteReg(IER, 0x02 | 0x01); + +  initlock(&uart_tx_lock, "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 +uartputc(int c, int block) +{ +  acquire(&uart_tx_lock); +  while(1){ +    if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ +      // 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; +    } +  }  } -// write one output character to the UART. +// 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 -uartputc(int c) +uartstart()  { -  // wait for Transmit Holding Empty to be set in LSR. -  while((ReadReg(LSR) & (1 << 5)) == 0) -    ; -  WriteReg(THR, c); +  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. @@ -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  uartintr(void)  { +  // read and process incoming characters.    while(1){      int c = uartgetc();      if(c == -1)        break;      consoleintr(c);    } + +  // send buffered characters. +  acquire(&uart_tx_lock); +  uartstart(); +  release(&uart_tx_lock);  } | 
