diff --git a/uart_echo/.gitignore b/uart_echo/.gitignore new file mode 100644 index 0000000..f5df561 --- /dev/null +++ b/uart_echo/.gitignore @@ -0,0 +1,4 @@ +build/* +core +a.out +host diff --git a/uart_echo/Makefile b/uart_echo/Makefile new file mode 100644 index 0000000..5c92987 --- /dev/null +++ b/uart_echo/Makefile @@ -0,0 +1,87 @@ +# Project setup +PROJ = uart +BUILD = ./build + +# This code is compatible with the devices +# 1k, short for iCE40 HX1K - the USB stick +# 8k, short for iCE40 HX8K - a small USB-connected development board +# Device-specific files are not overwritten, which which this Makefile +# looks just a bit uncomfortable at a first sight. + +# Uncomment the the line that you want to build the firmware for +DEVICE = 1k +#DEVICE = 8k + +#### nothing to configure below this line ##### + +# The FOOTPRINT describes what port of the FPGA are connected to what +# feature on the chip. This depends on the device, so this Makefile +# knows how to configures this. + +ifeq (8k,$(DEVICE)) +FOOTPRINT = ct256 +else +FOOTPRINT = tq144 +endif +STICKTTY = /dev/ttyUSB1 + +# Files +FILES = top.v +FILES += uart_tx.v +FILES += uart_rx.v +FILES += uart_clock.v + +FILEStest = uart_clock.v +FILEStest += uart_clock_top.v + +.PHONY: all clean burn + +all:$(BUILD)/$(PROJ)_$(DEVICE).bin host + + +CFLAGS += -g +host: host.c Makefile + $(CC) $(CFLAGS) -o host host.c + +$(BUILD)/$(PROJ).blif: $(FILES) Makefile + # if build folder doesn't exist, create it + mkdir -p $(BUILD) + # synthesize using Yosys + yosys -p "synth_ice40 -top top -blif $(BUILD)/$(PROJ).blif" $(FILES) + +$(BUILD)/$(PROJ)_$(DEVICE).asc: pinmap_$(FOOTPRINT).pcf $(BUILD)/$(PROJ).blif + # Place and route using arachne + arachne-pnr -d $(DEVICE) -P $(FOOTPRINT) -o $(BUILD)/$(PROJ)_$(DEVICE).asc \ + -p pinmap_$(FOOTPRINT).pcf $(BUILD)/$(PROJ).blif + +$(BUILD)/$(PROJ)_$(DEVICE).bin: $(BUILD)/$(PROJ)_$(DEVICE).asc + # Convert to bitstream using IcePack + icepack $(BUILD)/$(PROJ)_$(DEVICE).asc $(BUILD)/$(PROJ)_$(DEVICE).bin + +burn: $(BUILD)/$(PROJ)_$(DEVICE).bin + iceprog $< + +iverilog: $(BUILD)/$(PROJ).iverilog +$(BUILD)/$(PROJ).iverilog: $(FILES) + iverilog -o $(BUILD)/$(PROJ).iverilog $(FILES) + + +test: $(BUILD)/$(PROJ)test.bin +$(BUILD)/$(PROJ)test.bin: Makefile $(FILEStest) + iverilog -o $(BUILD)/test.a.out $(FILEStest) + mkdir -p $(BUILD) + yosys -p "synth_ice40 -top uart_clock_top -blif $(BUILD)/$(PROJ)test.blif" $(FILEStest) + arachne-pnr -d $(DEVICE) -P $(FOOTPRINT) -o $(BUILD)/$(PROJ)test.asc -p pinmap_$(FOOTPRINT).pcf $(BUILD)/$(PROJ)test.blif + icepack $(BUILD)/$(PROJ)test.asc $(BUILD)/$(PROJ)test.bin + +burntest: $(BUILD)/$(PROJ)test.bin + iceprog $< + +run: + sudo screen $(STICKTTY) + +run2: host + watch -n 0.2 sudo ./host /dev/ttyUSB1 some almost german-like long word + +clean: + rm -f build/* a.out core diff --git a/uart_echo/README.md b/uart_echo/README.md new file mode 100644 index 0000000..a3ebcbb --- /dev/null +++ b/uart_echo/README.md @@ -0,0 +1,41 @@ +This bitstream lets the FPGA control the serial line +setting of the FTDI chip, which again is connected to +the USB port. The iCE stick then behaves exactly like +a modem connected with an USB port and can be connected +to with a regular terminal emulator from any operating +system. + +An important parameter are the number of characters +per second to be transmitted, i.e. BAUD. This example +sets this to 9600 - which is slow. Under Linux, you may +use the following commands to address the FPGA once the +bitstream was burnt to it: + + * sudo minicom -b 9600 -D /dev/ttyUSB1 # or alternatively + * sudo screen /dev/ttyUSB1 + +If you need the 'sudo' depends on the permissions assigned +to the device. The use of screen may sound surprising to +many. It is a multifunctional tool. To become +familar with it is however a very good investment and +highly encouraged. Minicom you leave with "CTRL-A x", +screen with "CTRL-A d". + +Linux users may also run a small C program to perform the +I/O with the device, i.e. to prepare a subsequent embedding +of the device in programms running on the computer. + + * sudo ./host /dev/ttyUSB1 some text + +To evaluate the reliability of the communication, e.g. in a +virtualised environment, we propose to use the here provided +'host' utility repeatedly. The "run2" target of the Makefile +for instance combines it with an invocation of "watch" for a +manual inspection. + +The 'uart_echo' example was derived from the only writing +example 'uart_transmission' of Paul Martin after an idea +from Stefan Ziegenbalg at http://www.ztex.de/firmware-kit/example.e.html. +It was prepared by Steffen Möller and Ruben Undheim as a donation +to the ice40 example collection of Paul Martin under the +same current or future license as the reminder of his code base. diff --git a/uart_echo/host.c b/uart_echo/host.c new file mode 100644 index 0000000..6622606 --- /dev/null +++ b/uart_echo/host.c @@ -0,0 +1,136 @@ +// Example follows StackOverflow.com +// http://stackoverflow.com/questions/18108932/linux-c-serial-port-reading-writing +// +// Modified by Steffen Moeller, 2016, to work with the iCE40 HX1K + +#include // standard input / output functions +#include +#include // string function definitions +#include // UNIX standard function definitions +#include // File control definitions +#include // Error number definitions +#include // POSIX terminal control definitions + +static const unsigned char cmd[] = "INIT \r"; +static char response[2048]; + +char* const write_and_read(const int device, + char const * const writeme, + char * const readtome) { + + int n_written = 0, + spot_w = 0; + + int n_read = 0, + spot_r = 0; + char buf = '\0'; + + do { + + // Write: + + if (strlen(writeme)>n_written) { + n_written = write( device, writeme+spot_w, 1 ); + fprintf(stderr,"Written: %c\n", *(writeme+spot_w)); + spot_w += n_written; + } + + //It should definitely not be necessary to write byte per byte, + //also int n_written = write( device, cmd, sizeof(cmd) -1) would be nice to work + //It does not, though, our device answers to quickly. + // + // Since this is the echo firmware, and we only sent a single character, + // we also expect to read no more than a single character. + + // Read: + + n_read = read( device, &buf, 1 ); + sprintf( &readtome[spot_r], "%c", buf ); + fprintf(stderr,"Read: %c\n", readtome[spot_r]); + spot_r += n_read; + + if (n_read < 0) { + fprintf(stderr,"Error %d reading: %s\n", errno, strerror(errno)); + } + else if (n_read == 0) { + fprintf(stderr,"Read nothing!\n"); + } + } while (writeme[spot_w] != 0 && n_written > 0); + + readtome[spot_r]=0; + return(readtome); +} + + +char main(int argc, char *argv[]) { + + if (argc < 2 || 0==strcmp("-h",argv[1]) || 0==strcmp("--help",argv[1])) { + printf("Usage: %s some text\n",argv[0]); + exit(0); + } + + // open the device expected to be specified in first argument + + const int USB = open( argv[1], O_RDWR| O_NOCTTY ); + + if (USB<0) { + fprintf(stderr,"Error %d opening %s : %s\n", errno, argv[1], strerror (errno)); + exit(errno); + } + + struct termios tty; + memset (&tty, 0, sizeof tty); + + /* Error Handling */ + if ( tcgetattr ( USB, &tty ) != 0 ) { + fprintf(stderr, "Error %d from tcgetattr: %s\n", errno, strerror(errno)); + exit(errno); + } + + /* Set Baud Rate */ + int ospeed = cfsetospeed (&tty, (speed_t)B9600); + int ispeed = cfsetispeed (&tty, (speed_t)B9600); + // This is seemingly a bug in the man page - those routines return 0, no the speed + //fprintf(stderr,"Set speeds\n input: %d\n output: %d\n",ispeed,ospeed); + + /* Setting other Port Stuff */ + tty.c_cflag &= ~PARENB; // Make 8n1 + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CSIZE; + tty.c_cflag |= CS8; + + tty.c_cflag &= ~CRTSCTS; // no flow control + tty.c_cc[VMIN] = 1; // read doesn't block + tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines + + /* Make raw */ + cfmakeraw(&tty); + + /* Flush Port, then applies attributes */ + if (0 != tcflush( USB, TCIFLUSH )) { + fprintf(stderr, "Error %d from tcflush: %s\n", errno, strerror(errno)); + exit(errno); + } + + if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) { + fprintf(stderr, "Error %d from tcsetattr: %s\n", errno, strerror(errno)); + exit(errno); + } + + if (2 >= argc) { + // nothing to write specified by user + write_and_read(USB,cmd,response); + printf("Response: %s\n",response); + } else { + // iterating over all arguments provided + for(int a=2; a>1; + buf_rx[7] = rx; + //buf_rx = {rx,buf_rx[7:1]}; + bits_received <= bits_received + 1; + end else begin + // received the stop bit + state <= STATE_RXDONE; + rxbyte <= buf_rx; + //rxbyte <= 8'd69; + end + //rxbyte <= "0"+ bits_received; + end + + STATE_RXDONE: begin + state <= STATE_IDLE; + rxdone <= 1'b1; + end + + default: begin + state <= STATE_IDLE; + end + + endcase + + end // always + +endmodule diff --git a/uart_echo/uart_tx.v b/uart_echo/uart_tx.v new file mode 100644 index 0000000..a7fc730 --- /dev/null +++ b/uart_echo/uart_tx.v @@ -0,0 +1,74 @@ +// 8N1 UART Module, transmit only + +module uart_tx_8n1 ( + input wire clk, // input clock + output wire tx, // tx wire + input wire senddata, // trigger tx + input wire[7:0] txbyte, // outgoing byte + output reg txdone // outgoing byte sent + ); + + /* Parameters */ + parameter STATE_IDLE = 2'd0; + parameter STATE_STARTTX= 2'd1; + parameter STATE_TXING = 2'd2; + parameter STATE_TXDONE = 2'd3; + + /* State variables */ + reg[1:0] state= STATE_IDLE; + reg[7:0] buf_tx=8'b0; + reg[7:0] bits_sent=5'b0; + reg txbit=1'b1; + + /* Wiring */ + assign tx=txbit; + + /* always */ + always @ (posedge clk) begin + + case (state) + + STATE_IDLE: begin + // start sending? + if (senddata == 1) begin + state <= STATE_STARTTX; + buf_tx <= txbyte; + txdone <= 1'b0; + end else if (state == STATE_IDLE) begin + // idle at high + txbit <= 1'b1; + txdone <= 1'b0; + end + end + + STATE_STARTTX: begin + txbit <= 1'b0; + state <= STATE_TXING; + end + + STATE_TXING: begin + if (bits_sent < 5'd8) begin + txbit <= buf_tx[0]; + buf_tx <= buf_tx>>1; + bits_sent = bits_sent + 1; + end else begin + // send stop bit (high) + txbit <= 1'b1; + bits_sent <= 5'b0; + state <= STATE_TXDONE; + end + end + + STATE_TXDONE: begin + txdone <= 1'b1; + state <= STATE_IDLE; + end + + default: begin + state <= STATE_IDLE; + end + + endcase + end + +endmodule