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

Second example demonstrating input and output asynchronicity #3

Open
wants to merge 1 commit 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
*.bin
*.blif
*.tiles
*_host
a.out
34 changes: 29 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ PROG = iceprog
TOP = uart_demo.v
PCF = icestick.pcf
DEVICE = 1k
PATHTODEVICE = /dev/ttyUSB1

OUTPUT = $(patsubst %.v,%.bin,$(TOP))
BITSTREAM = $(patsubst %.v,%.bin,$(TOP))
HOST = $(patsubst %.v,%_host,$(TOP))

ifeq (uart_adder.v,$(TOP))
all: $(BITSTREAM) $(HOST)
else
all: $(BITSTREAM)
endif

all: $(OUTPUT)

%.bin: %.tiles
$(GEN) $< $@
Expand All @@ -21,9 +28,26 @@ all: $(OUTPUT)
$(SYN) -p "read_verilog $<; synth_ice40 -flatten -blif $@"

clean:
rm -f *.bin *.blif *.tiles
rm -f *.bin *.blif *.tiles uart_adder_host

flash: $(OUTPUT)
flash: $(BITSTREAM)
$(PROG) $<

.PHONY: all clean flash
run:
for i in $$(seq 0 3); do \
for j in $$(seq 0 3); do \
sudo ./uart_adder_host $(PATHTODEVICE) $$i $$j ; \
done ; \
done

iverilog:
iverilog $(TOP)

uart_demo_host:
# No action

uart_adder_host: uart_adder_host.c
$(CC) $(CFLAGS) -o $@ $<


.PHONY: all clean flash uart_demo_host
61 changes: 59 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,27 @@
[![Build Status](https://jenkins.cyrozap.com/job/iCEstick-UART-Demo/badge/icon)](https://jenkins.cyrozap.com/job/iCEstick-UART-Demo/)
[![License](http://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)

The iCEstick is a low-cost FPGA by Lattice Semiconductors. It
directly attaches to a computer's USB port and is ideal for smallish
(sub-)projects or to just learn about the technology. The community
provides a free pipeline to program the device. A major beginner's
hurdle remains to interact with the device. A common approach is to
implement a serial interface. This project provides such an UART with
a very clear terminology. It chose a straight-forward implementation
that is easily followed by starters, with two examples to lay out
how to embedd this functionality in applications.


## Prerequisites

- [Yosys][1]
- [arachne-pnr][2]
- [IceStorm][3]

For a quick setup, the Debian Linux distribution provides respective
[packages][4]. The iCEstick can be used in virtual environments like
VirtualBox.

## Building

git clone https://github.com/cyrozap/iCEstick-UART-Demo.git
Expand All @@ -18,10 +33,52 @@

## Flashing

Plug in your iCEstick, then run `make flash`. Depending on your permissions, you
may need to run it with `sudo`.
Plug in your iCEstick, then run `make flash`. Depending on your
permissions, you may need to run it with `sudo`.

## Running

This project provides two applications. These share the exact same UART
implementation, but have other differences for whwhich the iCEstick
needs to be reflashed when changinging between the two.

### uart\_demo

This implements an "echo": The character sent to the FPGA is immediately
returned. To investigate, start a terminal program like minicom or
teraterm, under Linux the tool screen is exceptionally handy. Check the
respective documentation to learn how to specify the device (/dev/ttyUSB0
or /dev/ttyUSB1) and the BAUD rate (9600).

### uart\_adder

After two bytes have been sent to the FPGA, the FPGA adds the two values
together and returns the input values and the sum. The communication
could still be performed with a terminal tool, but only a subset of
all byte values is displayed as ASCII. A small programm written in C,
uart\_adder\_host, performs the communication with the FPGA, instead. It
takes the path to the device, the first byte and the second as arguments
on the command line. It shows what is sent and what is received, byte
per byte.

It should be noted that all input data (the two bytes) are stored in a
single C struct and that data structure is copied bytewise under complete
neglect of the underlying data structure. This can be as easily decomposed
on the FPGA side, which would work for arbitrary data structures

On the FPGA-side, uart\_adder implements two layers of
finite-state-machines (FSM). The upper layer circles between the
receive-compute-send states. The reading and sending have substates for
each byte and the interim time it needs to receive/send it.

To run, do
```
make TOP=uart_adder.v flash
sudo ./uart_adder_host /dev/ttyUSB1 4 5
```


[1]: http://www.clifford.at/yosys/
[2]: https://github.com/cseed/arachne-pnr
[3]: http://www.clifford.at/icestorm/
[4]: http://wiki.debian.org/FPGA/Lattice
207 changes: 207 additions & 0 deletions uart_adder.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* Copyright 2015 Forest Crossman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

`include "cores/osdvu/uart.v"

module top(
input iCE_CLK,
input RS232_Rx_TTL,
output RS232_Tx_TTL,
output LED0,
output LED1,
output LED2,
output LED3,
output LED4
);

wire reset = 0;
reg transmit;
reg [7:0] tx_byte;
wire received;
wire [7:0] rx_byte;
wire is_receiving;
wire is_transmitting;
wire recv_error;

assign LED4 = recv_error;
//assign {LED3, LED2, LED1, LED0} = rx_byte[7:4];
assign {LED3, LED2, LED1, LED0} = rx_byte[3:0];

uart #(
.baud_rate(9600), // The baud rate in kilobits/s
.sys_clk_freq(12000000) // The master clock frequency
)
uart0(
.clk(iCE_CLK), // The master clock for this module
.rst(reset), // Synchronous reset
.rx(RS232_Rx_TTL), // Incoming serial line
.tx(RS232_Tx_TTL), // Outgoing serial line
.transmit(transmit), // Signal to transmit
.tx_byte(tx_byte), // Byte to transmit
.received(received), // Indicated that a byte has been received
.rx_byte(rx_byte), // Byte received
.is_receiving(is_receiving), // Low when receive line is idle
.is_transmitting(is_transmitting),// Low when transmit line is idle
.recv_error(recv_error) // Indicates error in receiving packet.
);

// input and output to be communicated
reg [15:0] vinput=16'b0; // input and output are reserved keywords
reg [23:0] voutput=24'b0;


reg [2:0] writecount=write_A;
reg [1:0] readcount =read_A;

parameter STATE_RECEIVING = 2'd0;
parameter STATE_CALCULATING = 2'd1;
parameter STATE_SENDING = 2'd2;
//parameter STATE_SEND_COMPLETED = 2'b11;

parameter read_A = 2'd0;
parameter read_A_transition_B = 2'd1;
parameter read_B = 2'd2;

parameter write_A = 3'd0;
parameter write_A_transit_B = 3'd1;
parameter write_B = 3'd2;
parameter write_B_transit_AplusB = 3'd3;
parameter write_AplusB = 3'd4;
parameter write_done = 3'd5;

reg ready=1;

reg [1:0] state=STATE_RECEIVING;



always @(posedge iCE_CLK) begin

case (state)

STATE_RECEIVING: begin
transmit <= 0;
case (readcount)
read_A: begin
if(received) begin
vinput[7:0]<=8'b0;
vinput[15:8]<=rx_byte;
readcount <= read_A_transition_B;
end
end

read_A_transition_B: begin
if(~received) begin
readcount <= read_B;
end
end

read_B: begin
if(received) begin
vinput[7:0]<=rx_byte;
state<=STATE_CALCULATING;
readcount <= read_A;
end
end

default: begin
// should not be reached
state<=STATE_CALCULATING;
end
endcase
end

STATE_CALCULATING: begin
writecount <= write_A;
voutput[7:0] <= vinput[15:8]+vinput[7:0];
voutput[23:8] <= vinput[15:0];
state <= STATE_SENDING;
end

STATE_SENDING: begin


case (writecount)

write_A: begin
if (~ is_transmitting) begin
writecount <= write_A_transit_B;
tx_byte <= voutput[23:16];
//tx_byte <= vinput[15:8];
transmit <= 1;
state <= STATE_SENDING;
end
end

write_A_transit_B: begin
if ( is_transmitting) begin
writecount <= write_B;
transmit <= 0;
end
end

write_B: begin
if (~ is_transmitting) begin
writecount <= write_B_transit_AplusB;
tx_byte <= voutput[15:8];
//tx_byte <= vinput[7:0];
transmit <= 1;
state <= STATE_SENDING;
end
end

write_B_transit_AplusB: begin
if ( is_transmitting) begin
transmit <= 0;
writecount <= write_AplusB;
end
end

write_AplusB: begin
if (~ is_transmitting) begin
tx_byte = voutput[7:0];
transmit <= 1;
writecount <= write_done;
state <= STATE_SENDING;
end
end

write_done: begin
if (~ is_transmitting) begin
writecount <= write_A;
state <= STATE_RECEIVING;
transmit <= 0;
end
end

endcase

end

default: begin
// should not be reached
state <= STATE_RECEIVING;
readcount <= read_A;
end

endcase

end



endmodule
Loading