interrupt-driven uart output, hopefully a nice example for teaching.
This commit is contained in:
		
							parent
							
								
									1e72d5ca08
								
							
						
					
					
						commit
						27057bc9b4
					
				
					 5 changed files with 100 additions and 18 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
uartputc(int c)
 | 
			
		||||
uartputc(int c, int block)
 | 
			
		||||
{
 | 
			
		||||
  // wait for Transmit Holding Empty to be set in LSR.
 | 
			
		||||
  while((ReadReg(LSR) & (1 << 5)) == 0)
 | 
			
		||||
    ;
 | 
			
		||||
  WriteReg(THR, c);
 | 
			
		||||
  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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue