Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uart echo - character sent to fpga returns back on screen #2

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions uart_echo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/*
core
a.out
host
87 changes: 87 additions & 0 deletions uart_echo/Makefile
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions uart_echo/README.md
Original file line number Diff line number Diff line change
@@ -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.
136 changes: 136 additions & 0 deletions uart_echo/host.c
Original file line number Diff line number Diff line change
@@ -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 <stdio.h> // standard input / output functions
#include <stdlib.h>
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // 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 <device> 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<argc; a++) {
write_and_read(USB,argv[a],response);
printf("Response: %s\n",response);
}
}

close(USB);

}

13 changes: 13 additions & 0 deletions uart_echo/pinmap_ct256.pcf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# example.pcf
set_io --warn-no-port led1 B5
set_io --warn-no-port led2 B4
set_io --warn-no-port led3 A2
set_io --warn-no-port led4 A1
set_io --warn-no-port led5 C5
set_io --warn-no-port led6 C4
set_io --warn-no-port led7 B3
set_io --warn-no-port led8 C3
set_io --warn-no-port hwclk J3
# FTDI / UART pins
set_io --warn-no-port tx B12
set_io --warn-no-port rx B10
10 changes: 10 additions & 0 deletions uart_echo/pinmap_tq144.pcf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# example.pcf
set_io --warn-no-port hwclk 21
set_io --warn-no-port led1 99
set_io --warn-no-port led2 98
set_io --warn-no-port led3 97
set_io --warn-no-port led4 96
#set_io --warn-no-port led5 95
# FTDI / UART pins
set_io --warn-no-port tx 8
set_io --warn-no-port rx 9
92 changes: 92 additions & 0 deletions uart_echo/top.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/* Top level module for keypad + UART demo */
module top (
// input hardware clock (12 MHz)
input wire hwclk,
// all LEDs
output wire led1,
output wire led2,
output wire led3,
output wire led4,
// UART lines
output wire tx,
input wire rx
);

// UART registers
wire [7:0] uart_rxbyte;
reg [7:0] uart_txbyte=8'b0;
reg uart_send = 1'b1; // do not send anything by default
reg uart_receive = 1'b1; // listen
wire uart_txed;
wire uart_rxed;

// LED register
reg ledval1 = 0;
reg ledval2 = 0;
reg ledval3 = 0;
reg ledval4 = 0;

wire clk_9600; // takes the clock triggeres generated in read

// wire[1:0] state;

/*
assign led3 = ~state[1];
assign led4 = ~state[0];
*/

// UART receiver module designed for
// 8 bits, no parity, 1 stop bit.
uart_rx_8n1 receiver (
.hwclk (hwclk),
.clk_9600 (clk_9600), // 9600 baud rate clock, triggered by reads from host
.rx (rx), // input UART rx pin
.recvdata (uart_receive), // allow any incoming bytes
.rxbyte (uart_rxbyte), // byte received
.rxdone (uart_rxed) // input: rx is finished
);

// UART transmitter module designed for
// 8 bits, no parity, 1 stop bit.
uart_tx_8n1 transmitter (
.clk (clk_9600), // 9600 baud rate clock
.tx (tx), // output UART tx pin
//.senddata (uart_rxed), // trigger a UART transmit on baud clock
//.senddata (1'b1), // trigger a UART transmit on baud clock
.senddata (uart_send), // trigger a UART transmit on baud clock
.txbyte (uart_txbyte), // byte to be transmitted
//.txbyte (8'd70), // byte to be transmitted
.txdone (uart_txed) // input: tx is finished
);




// Wiring
//assign led1=ledval1;
assign led2=ledval2;
assign led3=ledval3;
assign led4=ledval4;

always @(posedge clk_9600) begin
if(uart_rxed) begin
uart_txbyte <= uart_rxbyte;
uart_send <= 1;
ledval4 <= ~ledval4;
end
else begin
uart_send <= 0;
end

if(uart_txed) begin
ledval3 <= ~ledval3;
end

if(rx) begin
ledval2 <= ~ledval2;
end
end



endmodule
Loading