-
Notifications
You must be signed in to change notification settings - Fork 0
/
uart.cpp
152 lines (137 loc) · 3.95 KB
/
uart.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <cstdint>
#include "os.hh"
#include "platform.hh"
/*
* The UART control registers are memory-mapped at address UART0.
* This macro returns the address of one of the registers.
*/
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))
/*
* Reference
* [1]: TECHNICAL DATA ON 16550, http://byterunner.com/16550.html
*/
/*
* UART control registers map. see [1] "PROGRAMMING TABLE"
* note some are reused by multiple functions
* 0 (write mode): THR/DLL
* 1 (write mode): IER/DLM
*/
#define RHR 0 // Receive Holding Register (read mode)
#define THR 0 // Transmit Holding Register (write mode)
#define DLL 0 // LSB of Divisor Latch (write mode)
#define IER 1 // Interrupt Enable Register (write mode)
#define DLM 1 // MSB of Divisor Latch (write mode)
#define FCR 2 // FIFO Control Register (write mode)
#define ISR 2 // Interrupt Status Register (read mode)
#define LCR 3 // Line Control Register
#define MCR 4 // Modem Control Register
#define LSR 5 // Line Status Register
#define MSR 6 // Modem Status Register
#define SPR 7 // ScratchPad Register
/*
* POWER UP DEFAULTS
* IER = 0: TX/RX holding register interrupts are bith disabled
* ISR = 1: no interrupt penting
* LCR = 0
* MCR = 0
* LSR = 60 HEX
* MSR = BITS 0-3 = 0, BITS 4-7 = inputs
* FCR = 0
* TX = High
* OP1 = High
* OP2 = High
* RTS = High
* DTR = High
* RXRDY = High
* TXRDY = Low
* INT = Low
*/
/*
* LINE STATUS REGISTER (LSR)
* LSR BIT 0:
* 0 = no data in receive holding register or FIFO.
* 1 = data has been receive and saved in the receive holding register or FIFO.
* ......
* LSR BIT 5:
* 0 = transmit holding register is full. 16550 will not accept any data for
* transmission. 1 = transmitter hold register (or FIFO) is empty. CPU can load
* the next character.
* ......
*/
#define LSR_RX_READY (1 << 0)
#define LSR_TX_IDLE (1 << 5)
#define uart_read_reg(reg) (*(UART_REG(reg)))
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))
void uart_init() {
/* disable interrupts. */
uart_write_reg(IER, 0x00);
/*
* Setting baud rate. Just a demo here if we care about the divisor,
* but for our purpose [QEMU-virt], this doesn't really do anything.
*
* Notice that the divisor register DLL (divisor latch least) and DLM (divisor
* latch most) have the same base address as the receiver/transmitter and the
* interrupt enable register. To change what the base address points to, we
* open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
* (DLAB), which is bit index 7 of the Line Control Register (LCR).
*
* Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING
* TABLE". We use 38.4K when 1.8432 MHZ crystal, so the corresponding value
* is 3. And due to the divisor register is two bytes (16 bits), so we need to
* split the value of 3(0x0003) into two bytes, DLL stores the low byte,
* DLM stores the high byte.
*/
uint8_t lcr = uart_read_reg(LCR);
uart_write_reg(LCR, lcr | (1 << 7));
uart_write_reg(DLL, 0x03);
uart_write_reg(DLM, 0x00);
/*
* Continue setting the asynchronous data communication format.
* - number of the word length: 8 bits
* - number of stop bits:1 bit when word length is 8 bits
* - no parity
* - no break control
* - disabled baud latch
*/
lcr = 0;
uart_write_reg(LCR, lcr | (3 << 0));
/*
* enable receive interrupts.
*/
uint8_t ier = uart_read_reg(IER);
uart_write_reg(IER, ier | (1 << 0));
}
int uart_putc(const char ch) {
while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0)
;
return uart_write_reg(THR, ch);
}
void uart_puts(const char *s) {
while (*s) {
uart_putc(*s++);
}
}
/**
* Get char from RX
*/
int uart_getc(void) {
if (uart_read_reg(LSR) & LSR_RX_READY) {
return uart_read_reg(RHR);
} else {
return -1;
}
}
/**
* Uart trap handler
*/
void uart_isr() {
while (1) {
int c = uart_getc();
if (c == -1) {
break;
} else {
uart_putc((char)c);
uart_putc('\n');
}
}
}