From fd2a704e3aa2f6c16289b63af8600ce2fc484fb2 Mon Sep 17 00:00:00 2001 From: Erik Strand <erik.strand@cba.mit.edu> Date: Thu, 30 Sep 2021 19:58:15 -0400 Subject: [PATCH] Add iCE40 comms test --- comm/iCE40/.gitignore | 6 + comm/iCE40/Makefile | 29 ++++ comm/iCE40/icebreaker.pcf | 54 +++++++ comm/iCE40/ring.v | 144 +++++++++++++++++ comm/iCE40/uart.v | 319 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 552 insertions(+) create mode 100644 comm/iCE40/.gitignore create mode 100644 comm/iCE40/Makefile create mode 100644 comm/iCE40/icebreaker.pcf create mode 100644 comm/iCE40/ring.v create mode 100644 comm/iCE40/uart.v diff --git a/comm/iCE40/.gitignore b/comm/iCE40/.gitignore new file mode 100644 index 0000000..9bc6787 --- /dev/null +++ b/comm/iCE40/.gitignore @@ -0,0 +1,6 @@ +/*.asc +/*.bin +/*.json +/*.rpt +/*.nplog +/*.yslog diff --git a/comm/iCE40/Makefile b/comm/iCE40/Makefile new file mode 100644 index 0000000..5ac6cd2 --- /dev/null +++ b/comm/iCE40/Makefile @@ -0,0 +1,29 @@ +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 diff --git a/comm/iCE40/icebreaker.pcf b/comm/iCE40/icebreaker.pcf new file mode 100644 index 0000000..03088f2 --- /dev/null +++ b/comm/iCE40/icebreaker.pcf @@ -0,0 +1,54 @@ +# 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 diff --git a/comm/iCE40/ring.v b/comm/iCE40/ring.v new file mode 100644 index 0000000..d4b0d02 --- /dev/null +++ b/comm/iCE40/ring.v @@ -0,0 +1,144 @@ +// 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 diff --git a/comm/iCE40/uart.v b/comm/iCE40/uart.v new file mode 100644 index 0000000..6cc9c08 --- /dev/null +++ b/comm/iCE40/uart.v @@ -0,0 +1,319 @@ +// 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 -- GitLab