You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			254 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
			
		
		
	
	
			254 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
| #include <signal.h>
 | |
| #include <poll.h>
 | |
| #include <unistd.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| #include <termios.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| /* Should we exit simulation on ctrl-c or pass it through? */
 | |
| #define EXIT_ON_CTRL_C
 | |
| 
 | |
| #define BAUD 115200
 | |
| /* Round to nearest */
 | |
| #define BITWIDTH ((CLK_FREQUENCY+(BAUD/2))/BAUD)
 | |
| 
 | |
| /*
 | |
|  * Our UART uses 16x oversampling, so at 50 MHz and 115200 baud
 | |
|  * each sample is: 50000000/(115200*16) = 27 clock cycles. This
 | |
|  * means each bit is off by 0.47% so for 8 bits plus a start and
 | |
|  * stop bit the errors add to be 4.7%.
 | |
|  */
 | |
| static double error = 0.05;
 | |
| 
 | |
| enum state {
 | |
| 	IDLE, START_BIT, BITS, STOP_BIT, ERROR
 | |
| };
 | |
| 
 | |
| static enum state tx_state = IDLE;
 | |
| static unsigned long tx_countbits;
 | |
| static unsigned char tx_bits;
 | |
| static unsigned char tx_byte;
 | |
| static unsigned char tx_prev;
 | |
| 
 | |
| /*
 | |
|  * Return an error if the transition is not close enough to the start or
 | |
|  * the end of an expected bit.
 | |
|  */
 | |
| static bool is_error(unsigned long bits)
 | |
| {
 | |
| 	double e = 1.0 * tx_countbits / BITWIDTH;
 | |
| 
 | |
| 	if ((e <= (1.0-error)) && (e >= error))
 | |
| 		return true;
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| void uart_tx(unsigned char tx)
 | |
| {
 | |
| 	switch (tx_state) {
 | |
| 		case IDLE:
 | |
| 			if (tx == 0) {
 | |
| 				tx_state = START_BIT;
 | |
| 				tx_countbits = BITWIDTH;
 | |
| 				tx_bits = 0;
 | |
| 				tx_byte = 0;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case START_BIT:
 | |
| 			tx_countbits--;
 | |
| 			if (tx == 1) {
 | |
| 				if (is_error(tx_countbits)) {
 | |
| 					printf("START_BIT error %ld %ld\n", BITWIDTH, tx_countbits);
 | |
| 					tx_countbits = BITWIDTH*2;
 | |
| 					tx_state = ERROR;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (tx_countbits == 0) {
 | |
| 				tx_state = BITS;
 | |
| 				tx_countbits = BITWIDTH;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case BITS:
 | |
| 			tx_countbits--;
 | |
| 			if (tx_countbits == BITWIDTH/2) {
 | |
| 				tx_byte = tx_byte | (tx << tx_bits);
 | |
| 				tx_bits = tx_bits + 1;
 | |
| 			}
 | |
| 
 | |
| 			if (tx != tx_prev) {
 | |
| 				if (is_error(tx_countbits)) {
 | |
| 					printf("BITS error %ld %ld\n", BITWIDTH, tx_countbits);
 | |
| 					tx_countbits = BITWIDTH*2;
 | |
| 					tx_state = ERROR;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (tx_countbits == 0) {
 | |
| 				if (tx_bits == 8) {
 | |
| 					tx_state = STOP_BIT;
 | |
| 				}
 | |
| 				tx_countbits = BITWIDTH;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case STOP_BIT:
 | |
| 			tx_countbits--;
 | |
| 
 | |
| 			if (tx == 0) {
 | |
| 				if (is_error(tx_countbits)) {
 | |
| 					printf("STOP_BIT error %ld %ld\n", BITWIDTH, tx_countbits);
 | |
| 					tx_countbits = BITWIDTH*2;
 | |
| 					tx_state = ERROR;
 | |
| 					break;
 | |
| 				}
 | |
| 				/* Go straight to idle */
 | |
| 				write(STDOUT_FILENO, &tx_byte, 1);
 | |
| 				tx_state = IDLE;
 | |
| 			}
 | |
| 
 | |
| 			if (tx_countbits == 0) {
 | |
| 				write(STDOUT_FILENO, &tx_byte, 1);
 | |
| 				tx_state = IDLE;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case ERROR:
 | |
| 			tx_countbits--;
 | |
| 			if (tx_countbits == 0) {
 | |
| 				tx_state = IDLE;
 | |
| 			}
 | |
| 
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	tx_prev = tx;
 | |
| }
 | |
| 
 | |
| static struct termios oldt;
 | |
| 
 | |
| static void disable_raw_mode(void)
 | |
| {
 | |
| 	tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
 | |
| }
 | |
| 
 | |
| static void enable_raw_mode(void)
 | |
| {
 | |
| 	static bool initialized = false;
 | |
| 
 | |
| 	if (!initialized) {
 | |
| 		static struct termios newt;
 | |
| 
 | |
| 		tcgetattr(STDIN_FILENO, &oldt);
 | |
| 		newt = oldt;
 | |
| 		cfmakeraw(&newt);
 | |
| #ifdef EXIT_ON_CTRL_C
 | |
| 		newt.c_lflag |= ISIG;
 | |
| #endif
 | |
| 		tcsetattr(STDIN_FILENO, TCSANOW, &newt);
 | |
| 		initialized = true;
 | |
| 		atexit(disable_raw_mode);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int nonblocking_read(unsigned char *c)
 | |
| {
 | |
| 	int ret;
 | |
| 	unsigned long val = 0;
 | |
| 	struct pollfd fdset[1];
 | |
| 
 | |
| 	enable_raw_mode();
 | |
| 
 | |
| 	memset(fdset, 0, sizeof(fdset));
 | |
| 
 | |
| 	fdset[0].fd = STDIN_FILENO;
 | |
| 	fdset[0].events = POLLIN;
 | |
| 
 | |
| 	ret = poll(fdset, 1, 0);
 | |
| 	if (ret == 0)
 | |
| 		return false;
 | |
| 
 | |
| 	ret = read(STDIN_FILENO, &val, 1);
 | |
| 	if (ret != 1) {
 | |
| 		fprintf(stderr, "%s: read of stdin returns %d\n", __func__, ret);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	if (ret == 1) {
 | |
| 		*c = val;
 | |
| 		return true;
 | |
| 	} else {
 | |
| 		return false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static enum state rx_state = IDLE;
 | |
| static unsigned char rx_char;
 | |
| static unsigned long rx_countbits;
 | |
| static unsigned char rx_bit;
 | |
| static unsigned char rx = 1;
 | |
| 
 | |
| /* Avoid calling poll() too much */
 | |
| #define RX_INTERVAL 10000
 | |
| static unsigned long rx_sometimes;
 | |
| 
 | |
| unsigned char uart_rx(void)
 | |
| {
 | |
| 	unsigned char c;
 | |
| 
 | |
| 	switch (rx_state) {
 | |
| 		case IDLE:
 | |
| 			if (rx_sometimes++ >= RX_INTERVAL) {
 | |
| 				rx_sometimes = 0;
 | |
| 
 | |
| 				if (nonblocking_read(&c)) {
 | |
| 					rx_state = START_BIT;
 | |
| 					rx_char = c;
 | |
| 					rx_countbits = BITWIDTH;
 | |
| 					rx_bit = 0;
 | |
| 					rx = 0;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case START_BIT:
 | |
| 			rx_countbits--;
 | |
| 			if (rx_countbits == 0) {
 | |
| 				rx_state = BITS;
 | |
| 				rx_countbits = BITWIDTH;
 | |
| 				rx = rx_char & 1;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case BITS:
 | |
| 			rx_countbits--;
 | |
| 			if (rx_countbits == 0) {
 | |
| 				rx_bit = rx_bit + 1;
 | |
| 				if (rx_bit == 8) {
 | |
| 					rx = 1;
 | |
| 					rx_state = STOP_BIT;
 | |
| 				} else {
 | |
| 					rx = (rx_char >> rx_bit) & 1;
 | |
| 				}
 | |
| 				rx_countbits = BITWIDTH;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case STOP_BIT:
 | |
| 			rx_countbits--;
 | |
| 			if (rx_countbits == 0) {
 | |
| 				rx_state = IDLE;
 | |
| 			}
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	return rx;
 | |
| }
 |