Skip to content
Snippets Groups Projects
Commit fd2a704e authored by Erik Strand's avatar Erik Strand
Browse files

Add iCE40 comms test

parent c3065a2c
No related branches found
No related tags found
No related merge requests found
/*.asc
/*.bin
/*.json
/*.rpt
/*.nplog
/*.yslog
PROJ = ring
all: $(PROJ).rpt $(PROJ).bin
$(PROJ).json: $(PROJ).v
yosys -ql $(PROJ).yslog -p 'synth_ice40 -top top -json $@' $<
$(PROJ).asc: $(PROJ).json icebreaker.pcf
nextpnr-ice40 -ql $(PROJ).nplog --up5k --package sg48 --freq 39.75 --asc $@ --pcf icebreaker.pcf --json $<
$(PROJ).bin: $(PROJ).asc
icepack $< $@
$(PROJ).rpt: $(PROJ).asc
icetime -d up5k -c 39.75 -mtr $@ $<
prog: $(PROJ).bin
iceprog $<
sudo-prog: $(PROJ).bin
@echo 'Executing prog as root!!!'
sudo iceprog $<
clean:
rm -f $(PROJ).yslog $(PROJ).nplog $(PROJ).json $(PROJ).asc $(PROJ).rpt $(PROJ).bin
rm -f $(PROJ)_tb $(PROJ)_tb.vcd $(PROJ)_syn.v $(PROJ)_syntb $(PROJ)_syntb.vcd
.SECONDARY:
.PHONY: all prog clean
# 12 MHz clock
set_io -nowarn CLK_12MHZ 35
# RS232
set_io -nowarn RX 6
set_io -nowarn TX 9
# LEDs and Button
set_io -nowarn BTN_N 10
set_io -nowarn LEDR_N 11
set_io -nowarn LEDG_N 37
# RGB LED Driver
set_io -nowarn LED_RED_N 39
set_io -nowarn LED_GRN_N 40
set_io -nowarn LED_BLU_N 41
# SPI Flash
set_io -nowarn FLASH_SCK 15
set_io -nowarn FLASH_SSB 16
set_io -nowarn FLASH_IO0 14
set_io -nowarn FLASH_IO1 17
set_io -nowarn FLASH_IO2 12
set_io -nowarn FLASH_IO3 13
# PMOD 1A
set_io -nowarn P1A1 4
set_io -nowarn P1A2 2
set_io -nowarn P1A3 47
set_io -nowarn P1A4 45
set_io -nowarn P1A7 3
set_io -nowarn P1A8 48
set_io -nowarn P1A9 46
set_io -nowarn P1A10 44
# PMOD 1B
set_io -nowarn P1B1 43
set_io -nowarn P1B2 38
set_io -nowarn P1B3 34
set_io -nowarn P1B4 31
set_io -nowarn P1B7 42
set_io -nowarn P1B8 36
set_io -nowarn P1B9 32
set_io -nowarn P1B10 28
# LEDs and Buttons (PMOD 2)
set_io -nowarn LED1 26
set_io -nowarn LED2 27
set_io -nowarn LED3 25
set_io -nowarn LED4 23
set_io -nowarn LED5 21
set_io -nowarn BTN1 20
set_io -nowarn BTN2 19
set_io -nowarn BTN3 18
// ring.v
//
// iCE40 FPGA communication ring test
//
// Erik Strand 9/30/21
//
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose, but must
// acknowledge this project. Copyright is retained and
// must be preserved. The work is provided as is; no
// warranty is provided, and users accept all liability.
//
// Cause yosys to throw an error when we implicitly declare nets
`default_nettype none
`include "uart.v"
module top (
input CLK_12MHZ,
output P1A1,
input P1A2,
input BTN_N,
output P1A3,
output P1A4,
output LEDR_N,
output LEDG_N
);
// We use the PLL to generate a 39.75 MHz clock.
// Any faster than this and icetime complains.
wire clk;
wire clk_lock;
SB_PLL40_PAD #(
.FEEDBACK_PATH("SIMPLE"),
.PLLOUT_SELECT("GENCLK"),
.DIVR(4'b0000),
.DIVF(7'b0110100),
.DIVQ(3'b100),
.FILTER_RANGE(3'b001)
) pll_clk (
.PACKAGEPIN(CLK_12MHZ),
.PLLOUTGLOBAL(clk),
.LOCK(clk_lock),
.RESETB(1'b1),
.BYPASS(1'b0)
);
// We use BTN_N to start the loop.
// BTN_N is low when pressed.
// This code produces a 1 clock cycle pulse on btn_up_pulse whenever the button is released.
wire btn_pressed = !BTN_N;
reg btn_sample_1 = 1'b0;
reg btn_sample_2 = 1'b0;
reg btn_sample_3 = 1'b0;
reg btn_sample_4 = 1'b0;
reg btn_up_pulse = 1'b0;
assign LEDR_N = !btn_pressed;
always @(posedge clk or negedge clk_lock) begin
if (!clk_lock) begin
btn_sample_1 <= 1'b0;
btn_sample_2 <= 1'b0;
btn_sample_3 <= 1'b0;
btn_sample_4 <= 1'b0;
end else begin
btn_sample_1 <= btn_sample_2;
btn_sample_2 <= btn_sample_3;
btn_sample_3 <= btn_sample_4;
btn_sample_4 <= btn_pressed;
btn_up_pulse <= 1'b0;
if (btn_sample_1 && btn_sample_2 && !btn_sample_3 && !btn_sample_4) begin
btn_up_pulse <= 1'b1;
end
end
end
// This is the UART module we use to send byte data.
// For receiving data, we use 8x oversampling.
// This restricts our baud rate to one eight the clock rate.
reg respond = 1'b0;
wire is_transmitting;
wire transmit = (btn_up_pulse || respond) && !is_transmitting;
reg [7:0] tx_byte = 8'd0;
wire received;
wire [7:0] rx_byte;
wire is_receiving;
wire receive_error;
uart #(
// We're running a little slower than this.
// But it's only the ratio sys_clk_freq / baud_rate that matters anyway.
.baud_rate(5000000),
.sys_clk_freq(40000000)
) instance_name(
.clk(clk), // The master clock for this module
.rst(!clk_lock), // Synchronous reset
.rx(P1A2), // Incoming serial line
.tx(P1A1), // 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(receive_error) // Indicates error in receiving packet.
);
// We use pins P1A3 and P1A4 to monitor the loop.
// P1A3 goes high for one clock cycle when we receive a byte.
// It's a helpful scope trigger if you want to catch every round trip.
// P1A4 goes high for one clock cycle whenever we reset the sent byte.
// It's a helpful scope trigger if you want to catch every 256 round trips.
reg tx_byte_overflow_pulse = 1'b0;
assign P1A3 = received;
assign P1A4 = tx_byte_overflow_pulse;
// We toggle the LED every time we complete 256 round trips.
// If things are going well this isn't all that useful, since it happens nearly 1,000 times per
// second. But if you try to overclock it you'll know when it stops working since you'll see it
// blink sporadically.
reg led_reg = 1'b0;
assign LEDG_N = led_reg;
// This is the main loop.
always @(posedge clk or negedge clk_lock) begin
// These are pulses that should always be reset.
respond <= 1'b0;
tx_byte_overflow_pulse <= 1'b0;
if (!clk_lock) begin
led_reg <= 1'b0;
end else begin
if (received) begin
tx_byte <= rx_byte + 1;
if (tx_byte == 8'd255) begin
tx_byte <= 8'd0;
led_reg <= !led_reg;
tx_byte_overflow_pulse <= 1'b1;
end
respond <= 1'b1;
end
end
end
endmodule
// Documented Verilog UART
// Copyright (C) 2010 Timothy Goddard (tim@goddard.net.nz)
// 2013 Aaron Dahlen
// Distributed under the MIT licence.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//** INSTANTIATION ********************************************
//
// To instantiate this module copy this section to your main code...
//
// uart #(
// .baud_rate(baud_rate), // default is 9600
// .sys_clk_freq(sys_clk_freq) // default is 100000000
// )
// instance_name(
// .clk(clk), // The master clock for this module
// .rst(rst), // Synchronous reset
// .rx(rx), // Incoming serial line
// .tx(tx), // 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.
// //.recv_state(recv_state), // for test bench
// //.tx_state(tx_state) // for test bench
// );
//
`ifndef _dice_uart_v_
`define _dice_uart_v_
module uart(
input clk, // The master clock for this module
input rst, // Synchronous reset
input rx, // Incoming serial line
output tx, // Outgoing serial line
input transmit, // Assert to begin transmission
input [7:0] tx_byte, // Byte to transmit
output received, // Indicates that a byte has been received
output [7:0] rx_byte, // Byte received
output wire is_receiving, // Low when receive line is idle.
output wire is_transmitting,// Low when transmit line is idle.
output wire recv_error, // Indicates error in receiving packet.
output reg [3:0] rx_samples,
output reg [3:0] rx_sample_countdown
);
// The clock_divider is calculated using baud_rate and sys_clk_freq.
// To modify baud rate you can modify the defaults shown below or instantiate
// the module using the template shown in the INSTANTIATION section above.
// For aditional information about instantiation please see:
// http://www.sunburst-design.com/papers/CummingsHDLCON2002_Parameters_rev1_2.pdf
parameter baud_rate = 9600;
parameter sys_clk_freq = 100000000;
localparam one_baud_cnt = sys_clk_freq / (baud_rate);
//** SYMBOLIC STATE DECLARATIONS ******************************
localparam [2:0]
RX_IDLE = 3'd0,
RX_CHECK_START = 3'd1,
RX_SAMPLE_BITS = 3'd2,
RX_READ_BITS = 3'd3,
RX_CHECK_STOP = 3'd4,
RX_DELAY_RESTART = 3'd5,
RX_ERROR = 3'd6,
RX_RECEIVED = 3'd7;
localparam [1:0]
TX_IDLE = 2'd0,
TX_SENDING = 2'd1,
TX_DELAY_RESTART = 2'd2,
TX_RECOVER = 2'd3;
//** SIGNAL DECLARATIONS **************************************
reg [log2(one_baud_cnt * 16)-1:0] rx_clk;
reg [log2(one_baud_cnt * 2)-1:0] tx_clk;
reg [2:0] recv_state = RX_IDLE;
reg [3:0] rx_bits_remaining;
reg [7:0] rx_data;
reg tx_out = 1'b1;
reg [1:0] tx_state = TX_IDLE;
reg [3:0] tx_bits_remaining;
reg [7:0] tx_data;
//** ASSIGN STATEMENTS ****************************************
assign received = recv_state == RX_RECEIVED;
assign recv_error = recv_state == RX_ERROR;
assign is_receiving = recv_state != RX_IDLE;
assign rx_byte = rx_data;
assign tx = tx_out;
assign is_transmitting = tx_state != TX_IDLE;
//** TASKS / FUNCTIONS ****************************************
function integer log2(input integer M);
integer i;
begin
log2 = 1;
for (i = 0; 2**i <= M; i = i + 1)
log2 = i + 1;
end endfunction
//** Body *****************************************************
always @(posedge clk) begin
if (rst) begin
recv_state = RX_IDLE;
tx_state = TX_IDLE;
end
// Countdown timers for the receiving and transmitting
// state machines are decremented.
if(rx_clk) begin
rx_clk = rx_clk - 1'd1;
end
if(tx_clk) begin
tx_clk = tx_clk - 1'd1;
end
//** Receive state machine ************************************
case (recv_state)
RX_IDLE: begin
// A low pulse on the receive line indicates the
// start of data.
if (!rx) begin
// Wait 1/2 of the bit period
rx_clk = one_baud_cnt / 2;
recv_state = RX_CHECK_START;
end
end
RX_CHECK_START: begin
if (!rx_clk) begin
// Check the pulse is still there
if (!rx) begin
// Pulse still there - good
// Wait the bit period plus 3/8 of the next
rx_clk = (one_baud_cnt / 2) + (one_baud_cnt * 3) / 8;
rx_bits_remaining = 8;
recv_state = RX_SAMPLE_BITS;
rx_samples = 0;
rx_sample_countdown = 5;
end else begin
// Pulse lasted less than half the period -
// not a valid transmission.
recv_state = RX_ERROR;
end
end
end
RX_SAMPLE_BITS: begin
// sample the rx line multiple times
if (!rx_clk) begin
if (rx) begin
rx_samples = rx_samples + 1'd1;
end
rx_clk = one_baud_cnt / 8;
rx_sample_countdown = rx_sample_countdown -1'd1;
recv_state = rx_sample_countdown ? RX_SAMPLE_BITS : RX_READ_BITS;
end
end
RX_READ_BITS: begin
if (!rx_clk) begin
// Should be finished sampling the pulse here.
// Update and prep for next
if (rx_samples > 3) begin
rx_data = {1'd1, rx_data[7:1]};
end else begin
rx_data = {1'd0, rx_data[7:1]};
end
rx_clk = (one_baud_cnt * 3) / 8;
rx_samples = 0;
rx_sample_countdown = 5;
rx_bits_remaining = rx_bits_remaining - 1'd1;
if(rx_bits_remaining)begin
recv_state = RX_SAMPLE_BITS;
end else begin
recv_state = RX_CHECK_STOP;
rx_clk = one_baud_cnt / 2;
end
end
end
RX_CHECK_STOP: begin
if (!rx_clk) begin
// Should resume half-way through the stop bit
// This should be high - if not, reject the
// transmission and signal an error.
recv_state = rx ? RX_RECEIVED : RX_ERROR;
end
end
RX_ERROR: begin
// There was an error receiving.
// Raises the recv_error flag for one clock
// cycle while in this state and then waits
// 2 bit periods before accepting another
// transmission.
rx_clk = 8 * sys_clk_freq / (baud_rate);
recv_state = RX_DELAY_RESTART;
end
// why is this state needed? Why not go to idle and wait for next?
RX_DELAY_RESTART: begin
// Waits a set number of cycles before accepting
// another transmission.
recv_state = rx_clk ? RX_DELAY_RESTART : RX_IDLE;
end
RX_RECEIVED: begin
// Successfully received a byte.
// Raises the received flag for one clock
// cycle while in this state.
recv_state = RX_IDLE;
end
endcase
//** Transmit state machine ***********************************
case (tx_state)
TX_IDLE: begin
if (transmit) begin
// If the transmit flag is raised in the idle
// state, start transmitting the current content
// of the tx_byte input.
tx_data = tx_byte;
// Send the initial, low pulse of 1 bit period
// to signal the start, followed by the data
// tx_clk_divider = clock_divide;
tx_clk = one_baud_cnt;
tx_out = 0;
tx_bits_remaining = 8;
tx_state = TX_SENDING;
end
end
TX_SENDING: begin
if (!tx_clk) begin
if (tx_bits_remaining) begin
tx_bits_remaining = tx_bits_remaining - 1'd1;
tx_out = tx_data[0];
tx_data = {1'b0, tx_data[7:1]};
tx_clk = one_baud_cnt;
tx_state = TX_SENDING;
end else begin
// Set delay to send out 2 stop bits.
tx_out = 1;
tx_clk = 2 * one_baud_cnt;
tx_state = TX_DELAY_RESTART;
end
end
end
TX_DELAY_RESTART: begin
// Wait until tx_countdown reaches the end before
// we send another transmission. This covers the
// "stop bit" delay.
if (!tx_clk) begin
tx_state = TX_RECOVER;
end
end
TX_RECOVER: begin
// Wait unitil the transmit line is deactivated. This prevents repeated characters
tx_state = transmit ? TX_RECOVER : TX_IDLE;
end
endcase
end
endmodule
`endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment