Add interface2

This commit is contained in:
Andrew Kay
2021-03-29 16:38:00 -05:00
parent fce3b4c273
commit 925830f0a4
107 changed files with 54855 additions and 24 deletions

10
interface2/fpga/rtl/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
*.json
*.asc
*.bin
# iCEcube2
.mac_address
coax_Implmnt
synlog.tcl
*.log
*.log.bak

View File

@@ -0,0 +1 @@
include icecube2.mk

View File

@@ -0,0 +1,2 @@
create_clock -period 25.00 -name {clk} [get_ports {clk}]
create_clock -period 100.00 -name {spi_sck} [get_ports {spi_sck}]

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_buffer (
input clk,
input reset,
input [9:0] write_data,
input write_strobe,
output [9:0] read_data,
input read_strobe,
output empty,
output full,
output reg almost_empty,
output reg almost_full
);
parameter DEPTH = 256;
parameter ALMOST_EMPTY_THRESHOLD = 64;
parameter ALMOST_FULL_THRESHOLD = 192;
fifo_sync_ram #(
.DEPTH(DEPTH),
.WIDTH(10)
) fifo (
.wr_data(write_data),
.wr_ena(write_strobe),
.wr_full(full),
.rd_data(read_data),
.rd_ena(read_strobe),
.rd_empty(empty),
.clk(clk),
.rst(reset)
);
reg write_strobe_only;
reg read_strobe_only;
reg not_empty;
reg not_full;
always @(posedge clk)
begin
write_strobe_only <= (write_strobe && !read_strobe);
read_strobe_only <= (read_strobe && !write_strobe);
not_empty <= !empty;
not_full <= !full;
end
reg increment_level;
reg decrement_level;
always @(posedge clk)
begin
increment_level <= (write_strobe_only && not_full);
decrement_level <= (read_strobe_only && not_empty);
if (reset)
begin
increment_level <= 0;
decrement_level <= 0;
end
end
reg [$clog2(DEPTH):0] level;
always @(posedge clk)
begin
if (increment_level)
level <= level + 1;
else if (decrement_level)
level <= level - 1;
if (reset)
level <= 0;
end
always @(posedge clk)
begin
almost_empty <= (level <= ALMOST_EMPTY_THRESHOLD);
almost_full <= (level >= ALMOST_FULL_THRESHOLD);
end
endmodule

View File

@@ -0,0 +1,85 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_buffered_rx (
input clk,
input reset,
input rx,
output active,
output error,
output [9:0] data,
input read_strobe,
output empty,
output full,
input parity
);
parameter CLOCKS_PER_BIT = 8;
parameter DEPTH = 256;
localparam ERROR_OVERFLOW = 10'b0000001000;
wire coax_rx_error;
wire [9:0] coax_rx_data;
wire coax_rx_strobe;
coax_rx #(
.CLOCKS_PER_BIT(CLOCKS_PER_BIT)
) coax_rx (
.clk(clk),
.reset(reset),
.rx(rx),
.active(active),
.error(coax_rx_error),
.data(coax_rx_data),
.strobe(coax_rx_strobe),
.parity(parity)
);
wire [9:0] coax_buffer_data;
coax_buffer #(
.DEPTH(DEPTH)
) coax_buffer (
.clk(clk),
.reset(reset),
.write_data(coax_rx_data),
.write_strobe(coax_rx_strobe),
.read_data(coax_buffer_data),
.read_strobe(read_strobe && !error),
.empty(empty),
.full(full)
);
wire overflow;
assign overflow = ((active && !previous_active && !empty) || (coax_rx_strobe && full));
reg overflowed = 0;
reg previous_active;
always @(posedge clk)
begin
if (reset)
overflowed <= 0;
else if (overflow)
overflowed <= 1;
previous_active <= active;
end
assign error = overflow || overflowed || coax_rx_error;
assign data = (overflow || overflowed) ? ERROR_OVERFLOW : (coax_rx_error ? coax_rx_data : (empty ? 10'b0 : coax_buffer_data));
endmodule

View File

@@ -0,0 +1,151 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_buffered_tx (
input clk,
input reset,
output active,
output tx,
input [9:0] data,
input load_strobe,
input start_strobe,
output empty,
output full,
output reg ready,
input parity
);
parameter CLOCKS_PER_BIT = 8;
parameter DEPTH = 256;
parameter START_DEPTH = DEPTH * 0.75;
localparam STATE_IDLE = 0;
localparam STATE_TRANSMITTING_1 = 1;
localparam STATE_TRANSMITTING_2 = 2;
localparam STATE_TRANSMITTING_3 = 3;
reg [1:0] state = STATE_IDLE;
reg [1:0] next_state;
reg next_ready;
wire [9:0] coax_tx_data;
reg coax_tx_strobe = 0;
reg next_coax_tx_strobe;
wire coax_tx_ready;
coax_tx #(
.CLOCKS_PER_BIT(CLOCKS_PER_BIT)
) coax_tx (
.clk(clk),
.reset(reset),
.active(active),
.tx(tx),
.data(coax_tx_data),
.strobe(coax_tx_strobe),
.ready(coax_tx_ready),
.parity(parity)
);
reg coax_buffer_read_strobe = 0;
reg next_coax_buffer_read_strobe;
wire coax_buffer_almost_full;
coax_buffer #(
.DEPTH(DEPTH),
.ALMOST_FULL_THRESHOLD(START_DEPTH)
) coax_buffer (
.clk(clk),
.reset(reset),
.write_data(data),
.write_strobe(load_strobe),
.read_data(coax_tx_data),
.read_strobe(coax_buffer_read_strobe),
.empty(empty),
.almost_full(coax_buffer_almost_full),
.full(full)
);
always @(*)
begin
next_state = state;
next_coax_tx_strobe = 0;
next_coax_buffer_read_strobe = 0;
next_ready = 1;
case (state)
STATE_IDLE:
begin
// NOTE: Redundant check of almost full AND not empty is in
// order to protect against bugs with the almost full logic.
if ((start_strobe || coax_buffer_almost_full) && !empty)
next_state = STATE_TRANSMITTING_1;
end
STATE_TRANSMITTING_1:
begin
if (coax_tx_ready)
begin
if (!empty)
begin
next_coax_tx_strobe = 1;
next_state = STATE_TRANSMITTING_2;
end
else
begin
next_ready = 0;
next_state = STATE_TRANSMITTING_3;
end
end
end
STATE_TRANSMITTING_2:
begin
next_coax_buffer_read_strobe = 1;
next_state = STATE_TRANSMITTING_1;
end
STATE_TRANSMITTING_3:
begin
next_ready = 0;
if (!active)
next_state = STATE_IDLE;
end
endcase
end
always @(posedge clk)
begin
state <= next_state;
coax_tx_strobe <= next_coax_tx_strobe;
coax_buffer_read_strobe <= next_coax_buffer_read_strobe;
ready <= next_ready;
if (reset)
begin
state <= STATE_IDLE;
coax_tx_strobe <= 0;
coax_buffer_read_strobe <= 0;
ready <= 1;
end
end
endmodule

View File

@@ -0,0 +1,415 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_rx (
input clk,
input reset,
input rx,
output reg active,
output reg error,
output reg [9:0] data,
output reg strobe = 0,
input parity
);
parameter CLOCKS_PER_BIT = 8;
localparam ERROR_LOSS_OF_MID_BIT_TRANSITION = 10'b0000000001;
localparam ERROR_PARITY = 10'b0000000010;
localparam ERROR_INVALID_END_SEQUENCE = 10'b0000000100;
localparam STATE_IDLE = 0;
localparam STATE_START_SEQUENCE_1 = 1;
localparam STATE_START_SEQUENCE_2 = 2;
localparam STATE_START_SEQUENCE_3 = 3;
localparam STATE_START_SEQUENCE_4 = 4;
localparam STATE_START_SEQUENCE_5 = 5;
localparam STATE_START_SEQUENCE_6 = 6;
localparam STATE_START_SEQUENCE_7 = 7;
localparam STATE_START_SEQUENCE_8 = 8;
localparam STATE_START_SEQUENCE_9 = 9;
localparam STATE_SYNC_BIT = 10;
localparam STATE_DATA_BIT = 11;
localparam STATE_PARITY_BIT = 12;
localparam STATE_END_SEQUENCE_1 = 13;
localparam STATE_END_SEQUENCE_2 = 14;
localparam STATE_ERROR = 15;
reg [3:0] state = STATE_IDLE;
reg [3:0] next_state;
reg [7:0] state_counter;
reg [7:0] next_state_counter;
reg previous_rx;
reg bit_timer_reset = 0;
reg next_bit_timer_reset;
reg [9:0] next_data;
reg next_strobe;
reg [9:0] input_data;
reg [9:0] next_input_data;
reg input_data_parity;
reg [3:0] bit_counter = 0;
reg [3:0] next_bit_counter;
reg next_active;
reg next_error;
wire sample;
wire synchronized;
coax_rx_bit_timer #(
.CLOCKS_PER_BIT(CLOCKS_PER_BIT)
) bit_timer (
.clk(clk),
.rx(rx),
.reset(bit_timer_reset),
.sample(sample),
.synchronized(synchronized)
);
always @(*)
begin
next_state = state;
next_state_counter = state_counter + 1;
next_bit_timer_reset = 0;
next_data = data;
next_strobe = 0;
next_input_data = input_data;
next_bit_counter = bit_counter;
next_active = 0;
next_error = 0;
case (state)
STATE_IDLE:
begin
next_bit_timer_reset = 1;
if (!rx && previous_rx)
begin
next_state = STATE_START_SEQUENCE_1;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_1:
begin
if (sample)
begin
if (synchronized && rx)
next_state = STATE_START_SEQUENCE_2;
else
next_state = STATE_IDLE;
next_state_counter = 0;
end
else if (state_counter >= (CLOCKS_PER_BIT * 2))
begin
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_2:
begin
if (sample)
begin
if (synchronized && rx)
next_state = STATE_START_SEQUENCE_3;
else
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_3:
begin
if (sample)
begin
if (synchronized && rx)
next_state = STATE_START_SEQUENCE_4;
else
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_4:
begin
if (sample)
begin
if (synchronized && rx)
next_state = STATE_START_SEQUENCE_5;
else
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_5:
begin
if (sample)
begin
if (synchronized && rx)
next_state = STATE_START_SEQUENCE_6;
else
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_6:
begin
if (!rx)
begin
next_state = STATE_START_SEQUENCE_7;
next_state_counter = 0;
end
else if (state_counter >= CLOCKS_PER_BIT)
begin
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_7:
begin
if (rx)
begin
next_state = STATE_START_SEQUENCE_8;
next_state_counter = 0;
end
else if (state_counter >= (CLOCKS_PER_BIT * 2))
begin
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_8:
begin
if (!rx)
begin
next_bit_timer_reset = 1;
next_state = STATE_START_SEQUENCE_9;
next_state_counter = 0;
end
else if (state_counter >= (CLOCKS_PER_BIT * 2))
begin
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_START_SEQUENCE_9:
begin
// This is really the first STATE_SYNC_BIT but we treat it
// differently and consider it part of the start
// sequence.
if (sample && synchronized)
begin
if (rx)
begin
next_bit_counter = 0;
next_state = STATE_DATA_BIT;
end
else
begin
next_state = STATE_IDLE;
end
next_state_counter = 0;
end
else if (state_counter >= CLOCKS_PER_BIT)
begin
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_SYNC_BIT:
begin
next_active = 1;
if (sample)
begin
if (synchronized)
begin
if (rx)
begin
next_bit_counter = 0;
next_state = STATE_DATA_BIT;
end
else
begin
next_state = STATE_END_SEQUENCE_1;
end
next_state_counter = 0;
end
else
begin
next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION;
next_state = STATE_ERROR;
next_state_counter = 0;
end
end
end
STATE_DATA_BIT:
begin
next_active = 1;
if (sample)
begin
if (synchronized)
begin
next_input_data = { input_data[8:0], rx };
if (bit_counter < 9)
begin
next_bit_counter = bit_counter + 1;
end
else
begin
next_state = STATE_PARITY_BIT;
end
next_state_counter = 0;
end
else
begin
next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION;
next_state = STATE_ERROR;
next_state_counter = 0;
end
end
end
STATE_PARITY_BIT:
begin
next_active = 1;
if (sample)
begin
if (synchronized)
begin
if (rx == input_data_parity)
begin
next_strobe = 1;
next_data = input_data;
next_state = STATE_SYNC_BIT;
end
else
begin
next_data = ERROR_PARITY;
next_state = STATE_ERROR;
end
next_state_counter = 0;
end
else
begin
next_data = ERROR_LOSS_OF_MID_BIT_TRANSITION;
next_state = STATE_ERROR;
next_state_counter = 0;
end
end
end
STATE_END_SEQUENCE_1:
begin
if (rx)
begin
next_state = STATE_END_SEQUENCE_2;
next_state_counter = 0;
end
else if (state_counter >= CLOCKS_PER_BIT)
begin
next_data = ERROR_INVALID_END_SEQUENCE;
next_state = STATE_ERROR;
next_state_counter = 0;
end
end
STATE_END_SEQUENCE_2:
begin
// TODO: should this go to ERROR on timeout?
if (!rx)
begin
next_state = STATE_IDLE;
next_state_counter = 0;
end
else if (state_counter >= (CLOCKS_PER_BIT * 2))
begin
next_state = STATE_IDLE;
next_state_counter = 0;
end
end
STATE_ERROR:
begin
next_error = 1;
end
endcase
end
always @(posedge clk)
begin
state <= next_state;
state_counter <= next_state_counter;
bit_timer_reset <= next_bit_timer_reset;
data <= next_data;
strobe <= next_strobe;
input_data <= next_input_data;
// Parity includes the sync bit.
input_data_parity <= (parity == 1 ? ^{ 1'b1, input_data } : ~^{ 1'b1, input_data });
bit_counter <= next_bit_counter;
active <= next_active;
error <= next_error;
if (reset)
begin
bit_timer_reset <= 1;
state <= STATE_IDLE;
strobe <= 0;
active <= 0;
error <= 0;
end
previous_rx <= rx;
end
endmodule

View File

@@ -0,0 +1,111 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_rx_bit_timer (
input clk,
input rx,
input reset,
output reg sample,
output reg synchronized
);
parameter CLOCKS_PER_BIT = 8;
localparam IDLE = 0;
localparam SYNCHRONIZED = 1;
localparam UNSYNCHRONIZED = 2;
reg [1:0] state = IDLE;
reg [1:0] next_state;
reg previous_rx;
reg [$clog2(CLOCKS_PER_BIT*2):0] transition_counter = 0;
reg [$clog2(CLOCKS_PER_BIT*2):0] next_transition_counter;
reg [$clog2(CLOCKS_PER_BIT):0] bit_counter = 0;
reg [$clog2(CLOCKS_PER_BIT):0] next_bit_counter;
always @(*)
begin
next_state = state;
sample = 0;
synchronized = 0;
next_transition_counter = transition_counter;
next_bit_counter = bit_counter;
case (state)
IDLE:
begin
if (rx != previous_rx)
begin
next_transition_counter = 0;
next_bit_counter = CLOCKS_PER_BIT / 2;
next_state = SYNCHRONIZED;
end
end
SYNCHRONIZED:
begin
if (transition_counter < (CLOCKS_PER_BIT + (CLOCKS_PER_BIT / 4)))
next_transition_counter = transition_counter + 1;
else
next_state = UNSYNCHRONIZED;
synchronized = 1;
if (bit_counter < CLOCKS_PER_BIT)
next_bit_counter = bit_counter + 1;
else
next_bit_counter = 0;
if (rx != previous_rx && transition_counter > (CLOCKS_PER_BIT / 2))
begin
next_transition_counter = 0;
next_bit_counter = CLOCKS_PER_BIT / 2;
end
if (bit_counter == ((CLOCKS_PER_BIT / 4) * 3))
sample = 1;
end
UNSYNCHRONIZED:
begin
if (bit_counter < CLOCKS_PER_BIT)
next_bit_counter = bit_counter + 1;
else
next_bit_counter = 0;
if (bit_counter == ((CLOCKS_PER_BIT / 4) * 3))
sample = 1;
end
endcase
end
always @(posedge clk)
begin
state <= next_state;
transition_counter <= next_transition_counter;
bit_counter <= next_bit_counter;
if (reset)
state <= IDLE;
previous_rx <= rx;
end
endmodule

View File

@@ -0,0 +1,71 @@
#-- Synopsys, Inc.
#-- Project file /home/andrew/lattice_coax/coax/coax_syn.prj
#project files
add_file -verilog -lib work "coax_buffered_rx.v"
add_file -verilog -lib work "coax_rx.v"
add_file -verilog -lib work "coax_rx_bit_timer.v"
add_file -verilog -lib work "coax_tx.v"
add_file -verilog -lib work "coax_tx_bit_timer.v"
add_file -verilog -lib work "coax_tx_distorter.v"
add_file -verilog -lib work "control.v"
add_file -verilog -lib work "spi_device.v"
add_file -verilog -lib work "coax_buffered_tx.v"
add_file -verilog -lib work "coax_buffer.v"
add_file -verilog -lib work "third_party/fifo_sync_ram.v"
add_file -verilog -lib work "third_party/ram_sdp.v"
add_file -verilog -lib work "strobe_cdc.v"
add_file -verilog -lib work "top.v"
add_file -verilog -lib work "dual_clock_spi_device.v"
add_file -constraint -lib work "clocks.sdc"
#implementation: "coax_Implmnt"
impl -add coax_Implmnt -type fpga
#implementation attributes
set_option -vlog_std v2001
set_option -project_relative_includes 1
#device options
set_option -technology SBTiCE40UP
set_option -part iCE40UP5K
set_option -package SG48
set_option -speed_grade
set_option -part_companion ""
#compilation/mapping options
# mapper_options
set_option -frequency auto
set_option -write_verilog 0
set_option -write_vhdl 0
# Silicon Blue iCE40UP
set_option -maxfan 10000
set_option -disable_io_insertion 0
set_option -pipe 1
set_option -retiming 0
set_option -update_models_cp 0
set_option -fixgatedclocks 2
set_option -fixgeneratedclocks 0
# NFilter
set_option -popfeed 0
set_option -constprop 0
set_option -createhierarchy 0
# sequential_optimization_options
set_option -symbolic_fsm_compiler 1
# Compiler Options
set_option -compiler_compatible 0
set_option -resource_sharing 1
#automatic place and route (vendor) options
set_option -write_apr_constraint 1
#set result format/file last
project -result_format "edif"
project -result_file ./coax_Implmnt/coax.edf
project -log_file "./coax_Implmnt/coax.srr"
impl -active coax_Implmnt
project -run synthesis -clean

View File

@@ -0,0 +1,308 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_tx (
input clk,
input reset,
output reg active,
output reg tx,
input [9:0] data,
input strobe,
output ready,
input parity
);
parameter CLOCKS_PER_BIT = 8;
localparam IDLE = 0;
localparam START_SEQUENCE_1 = 1;
localparam START_SEQUENCE_2 = 2;
localparam START_SEQUENCE_3 = 3;
localparam START_SEQUENCE_4 = 4;
localparam START_SEQUENCE_5 = 5;
localparam START_SEQUENCE_6 = 6;
localparam START_SEQUENCE_7 = 7;
localparam START_SEQUENCE_8 = 8;
localparam START_SEQUENCE_9 = 9;
localparam SYNC_BIT = 10;
localparam DATA_BIT = 11;
localparam PARITY_BIT = 12;
localparam END_SEQUENCE_1 = 13;
localparam END_SEQUENCE_2 = 14;
localparam END_SEQUENCE_3 = 15;
reg [3:0] state = IDLE;
reg [3:0] next_state;
reg next_tx;
reg end_sequence;
reg next_end_sequence;
reg [9:0] output_data;
reg [9:0] next_output_data;
reg output_data_full = 0;
reg next_output_data_full;
reg parity_bit;
reg next_parity_bit;
reg output_parity_bit;
reg next_output_parity_bit;
reg [3:0] bit_counter = 0;
reg [3:0] next_bit_counter;
reg bit_timer_reset = 0;
reg next_bit_timer_reset;
wire first_half;
wire second_half;
wire last_clock;
coax_tx_bit_timer #(
.CLOCKS_PER_BIT(CLOCKS_PER_BIT)
) bit_timer (
.clk(clk),
.reset(bit_timer_reset),
.first_half(first_half),
.second_half(second_half),
.last_clock(last_clock)
);
always @(*)
begin
next_state = state;
next_tx = 0;
next_end_sequence = 0;
next_output_data = output_data;
next_output_data_full = output_data_full;
next_parity_bit = parity_bit;
next_output_parity_bit = output_parity_bit;
if (strobe && ready)
begin
next_output_data = data;
next_output_data_full = 1;
// Parity includes the sync bit.
next_parity_bit = (parity == 1 ? ^{ 1'b1, data } : ~^{ 1'b1, data });
end
next_bit_counter = bit_counter;
next_bit_timer_reset = 0;
case (state)
IDLE:
begin
next_bit_timer_reset = 1;
if (output_data_full)
begin
next_state = START_SEQUENCE_1;
end
end
START_SEQUENCE_1:
begin
next_tx = 1;
// TODO... off by 1
if (second_half)
begin
next_bit_timer_reset = 1;
next_state = START_SEQUENCE_2;
end
end
START_SEQUENCE_2:
begin
next_tx = first_half ? 0 : 1;
if (last_clock)
next_state = START_SEQUENCE_3;
end
START_SEQUENCE_3:
begin
next_tx = first_half ? 0 : 1;
if (last_clock)
next_state = START_SEQUENCE_4;
end
START_SEQUENCE_4:
begin
next_tx = first_half ? 0 : 1;
if (last_clock)
next_state = START_SEQUENCE_5;
end
START_SEQUENCE_5:
begin
next_tx = first_half ? 0 : 1;
if (last_clock)
next_state = START_SEQUENCE_6;
end
START_SEQUENCE_6:
begin
next_tx = first_half ? 0 : 1;
if (last_clock)
next_state = START_SEQUENCE_7;
end
START_SEQUENCE_7:
begin
next_tx = 0;
if (last_clock)
next_state = START_SEQUENCE_8;
end
START_SEQUENCE_8:
begin
next_tx = first_half ? 0 : 1;
if (last_clock)
next_state = START_SEQUENCE_9;
end
START_SEQUENCE_9:
begin
next_tx = 1;
if (last_clock)
next_state = SYNC_BIT;
end
SYNC_BIT:
begin
next_tx = first_half ? 0 : 1;
next_bit_counter = 9;
if (last_clock)
next_state = DATA_BIT;
end
DATA_BIT:
begin
next_tx = first_half ? ~output_data[9] : output_data[9];
if (last_clock)
begin
next_output_data = { output_data[8:0], output_data[9] };
next_bit_counter = bit_counter - 1;
if (bit_counter == 0)
begin
next_output_data_full = 0;
next_output_parity_bit = parity_bit;
next_state = PARITY_BIT;
end
end
end
PARITY_BIT:
begin
next_tx = first_half ? ~output_parity_bit : output_parity_bit;
if (last_clock)
begin
if (output_data_full)
begin
next_state = SYNC_BIT;
end
else
begin
next_end_sequence = 1;
next_state = END_SEQUENCE_1;
end
end
end
END_SEQUENCE_1:
begin
next_tx = first_half ? 1 : 0;
next_end_sequence = 1;
if (last_clock)
next_state = END_SEQUENCE_2;
end
END_SEQUENCE_2:
begin
next_tx = 1;
next_end_sequence = 1;
if (last_clock)
next_state = END_SEQUENCE_3;
end
END_SEQUENCE_3:
begin
next_tx = 1;
next_end_sequence = 1;
if (last_clock)
begin
next_tx = 0;
next_end_sequence = 0;
next_state = IDLE;
end
end
endcase
end
always @(posedge clk)
begin
state <= next_state;
active <= (state != IDLE); // TODO: this causes active to remain high one additional clock cycle
tx <= next_tx;
end_sequence <= next_end_sequence;
output_data <= next_output_data;
output_data_full <= next_output_data_full;
parity_bit <= next_parity_bit;
output_parity_bit <= next_output_parity_bit;
bit_counter <= next_bit_counter;
bit_timer_reset <= next_bit_timer_reset;
if (reset)
begin
state <= IDLE;
active <= 0;
tx <= 0;
end_sequence <= 0;
output_data_full <= 0;
end
end
assign ready = (!output_data_full && !end_sequence);
endmodule

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_tx_bit_timer (
input clk,
input reset,
output first_half,
output second_half,
output last_clock
);
parameter CLOCKS_PER_BIT = 8;
reg [$clog2(CLOCKS_PER_BIT):0] counter = 0;
reg [$clog2(CLOCKS_PER_BIT):0] next_counter;
always @(*)
begin
next_counter = last_clock ? 0 : counter + 1;
end
always @(posedge clk)
begin
counter <= next_counter;
if (reset)
counter <= 0;
end
assign first_half = (counter < CLOCKS_PER_BIT / 2);
assign second_half = ~first_half;
assign last_clock = (counter == CLOCKS_PER_BIT - 1);
endmodule

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module coax_tx_distorter (
input clk,
input active_input,
input tx_input,
output reg active_output,
output reg tx_output,
output reg tx_delay,
output reg tx_n
);
parameter CLOCKS_PER_BIT = 8;
localparam DELAY_CLOCKS = CLOCKS_PER_BIT / 4;
reg [DELAY_CLOCKS-1:0] tx_d = { (DELAY_CLOCKS){1'b1} };
always @(posedge clk)
begin
if (active_input)
begin
tx_d <= { tx_d[DELAY_CLOCKS-2:0], tx_input };
active_output <= 1;
tx_output <= tx_input;
tx_delay <= tx_d[DELAY_CLOCKS-1];
tx_n <= ~tx_input;
end
else
begin
tx_d <= { (DELAY_CLOCKS){1'b1} };
active_output <= 0;
tx_output <= 0;
tx_delay <= 0;
tx_n <= 0;
end
end
endmodule

View File

@@ -0,0 +1,347 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module control (
input clk,
input reset,
// SPI
input spi_cs_n,
input [7:0] spi_rx_data,
input spi_rx_strobe,
output reg [7:0] spi_tx_data,
output reg spi_tx_strobe,
output loopback,
// TX
output reg tx_reset,
input tx_active,
output reg [9:0] tx_data,
output reg tx_load_strobe,
output reg tx_start_strobe,
input tx_empty,
input tx_full,
input tx_ready,
output tx_parity,
// RX
output reg rx_reset,
input rx_active,
input rx_error,
input [9:0] rx_data,
output reg rx_read_strobe,
input rx_empty,
output rx_parity
);
parameter DEFAULT_CONTROL_REGISTER = 8'b01001000;
localparam STATE_IDLE = 0;
localparam STATE_READ_REGISTER_1 = 1;
localparam STATE_READ_REGISTER_2 = 2;
localparam STATE_WRITE_REGISTER_1 = 3;
localparam STATE_WRITE_REGISTER_2 = 4;
localparam STATE_TX_1 = 5;
localparam STATE_TX_2 = 6;
localparam STATE_TX_3 = 7;
localparam STATE_RX_1 = 8;
localparam STATE_RX_2 = 9;
localparam STATE_RX_3 = 10;
localparam STATE_RX_4 = 11;
localparam STATE_RESET = 12;
reg [7:0] state = STATE_IDLE;
reg [7:0] next_state;
reg [7:0] control_register = DEFAULT_CONTROL_REGISTER;
reg [7:0] next_control_register;
reg [7:0] register_mask;
reg [7:0] next_register_mask;
reg [7:0] command;
reg [7:0] next_command;
reg [7:0] next_spi_tx_data;
reg next_spi_tx_strobe;
reg next_tx_reset;
reg previous_tx_active;
reg [9:0] next_tx_data;
reg tx_data_valid = 0;
reg next_tx_data_valid;
reg next_tx_load_strobe;
reg next_tx_start_strobe;
reg tx_complete = 0;
reg next_tx_complete;
reg next_rx_reset;
reg next_rx_read_strobe;
reg [15:0] rx_buffer;
reg [15:0] next_rx_buffer;
reg [1:0] spi_cs_n_d;
always @(posedge clk)
begin
spi_cs_n_d <= { spi_cs_n_d[0], spi_cs_n };
end
always @(*)
begin
next_state = state;
next_control_register = control_register;
next_register_mask = register_mask;
next_command = command;
next_spi_tx_data = spi_tx_data;
next_spi_tx_strobe = 0;
next_tx_reset = 0;
next_tx_data = tx_data;
next_tx_data_valid = tx_data_valid;
next_tx_load_strobe = 0;
next_tx_start_strobe = 0;
next_tx_complete = tx_complete;
next_rx_reset = 0;
next_rx_read_strobe = 0;
next_rx_buffer = rx_buffer;
case (state)
STATE_IDLE:
begin
if (spi_rx_strobe)
begin
next_command = spi_rx_data;
case (spi_rx_data[3:0])
4'h2: next_state = STATE_READ_REGISTER_1;
4'h3: next_state = STATE_WRITE_REGISTER_1;
4'h4: next_state = STATE_TX_1;
4'h5: next_state = STATE_RX_1;
4'hf: next_state = STATE_RESET;
endcase
end
end
STATE_READ_REGISTER_1:
begin
next_spi_tx_data = 0;
case (command[7:4])
4'h1: next_spi_tx_data = { 1'b0, rx_error, rx_active, 1'b0, tx_complete, tx_active, 2'b0 };
4'h2: next_spi_tx_data = control_register;
4'hf: next_spi_tx_data = 8'ha5;
endcase
next_spi_tx_strobe = 1;
next_state = STATE_READ_REGISTER_2;
end
STATE_READ_REGISTER_2:
begin
if (spi_rx_strobe)
next_state = STATE_READ_REGISTER_1;
end
STATE_WRITE_REGISTER_1:
begin
if (spi_rx_strobe)
begin
next_register_mask = spi_rx_data;
next_state = STATE_WRITE_REGISTER_2;
end
end
STATE_WRITE_REGISTER_2:
begin
if (spi_rx_strobe)
begin
case (command[7:4])
4'h2: next_control_register = (control_register & ~register_mask) | (spi_rx_data & register_mask);
endcase
next_state = STATE_IDLE;
end
end
STATE_TX_1:
begin
next_tx_complete = 0;
next_state = STATE_TX_2;
end
STATE_TX_2:
begin
if (spi_rx_strobe)
begin
next_tx_data_valid = 0;
if (tx_full)
begin
next_spi_tx_data = 8'b10000001; // Overflow
next_spi_tx_strobe = 1;
end
else if (!tx_ready)
begin
next_spi_tx_data = 8'b10000010; // Underflow
next_spi_tx_strobe = 1;
end
else
begin
next_tx_data = { spi_rx_data[1:0], 8'b00000000 };
next_tx_data_valid = 1;
next_spi_tx_data = 8'h00;
next_spi_tx_strobe = 1;
end
next_state = STATE_TX_3;
end
end
STATE_TX_3:
begin
if (spi_rx_strobe)
begin
next_tx_data = { tx_data[9:8], spi_rx_data };
next_tx_load_strobe = tx_data_valid;
next_state = STATE_TX_2;
end
end
STATE_RX_1:
begin
next_rx_buffer = { rx_error, rx_empty, 4'b0000, rx_data };
next_state = STATE_RX_2;
end
STATE_RX_2:
begin
next_spi_tx_data = rx_buffer[15:8];
next_spi_tx_strobe = 1;
next_state = STATE_RX_3;
end
STATE_RX_3:
begin
if (spi_rx_strobe)
begin
next_spi_tx_data = rx_buffer[7:0];
next_spi_tx_strobe = 1;
// Reset on error and only dequeue if not empty.
if (rx_buffer[15])
next_rx_reset = 1; // TODO: should this be more explicit?
else if (!rx_buffer[14])
next_rx_read_strobe = 1;
next_state = STATE_RX_4;
end
end
STATE_RX_4:
begin
if (spi_rx_strobe)
next_state = STATE_RX_1;
end
STATE_RESET:
begin
// TODO: should this also reset the control register flags
// like loopback?
next_tx_reset = 1;
next_tx_complete = 0;
next_rx_reset = 1;
next_state = STATE_IDLE;
end
endcase
if (spi_cs_n_d[1])
begin
if (!tx_empty && !tx_active)
next_tx_start_strobe = 1; // TODO: this can last for multiple clock cycles, but that is okay...
next_state = STATE_IDLE;
end
if (!tx_active && previous_tx_active)
next_tx_complete = 1;
end
always @(posedge clk)
begin
state <= next_state;
control_register <= next_control_register;
register_mask <= next_register_mask;
command <= next_command;
spi_tx_data <= next_spi_tx_data;
spi_tx_strobe <= next_spi_tx_strobe;
tx_reset <= next_tx_reset;
tx_data <= next_tx_data;
tx_data_valid <= next_tx_data_valid;
tx_load_strobe <= next_tx_load_strobe;
tx_start_strobe <= next_tx_start_strobe;
tx_complete <= next_tx_complete;
rx_reset <= next_rx_reset;
rx_read_strobe <= next_rx_read_strobe;
rx_buffer <= next_rx_buffer;
if (reset)
begin
state <= STATE_IDLE;
control_register <= DEFAULT_CONTROL_REGISTER;
command <= 0;
spi_tx_data <= 0;
spi_tx_strobe <= 0;
tx_reset <= 0;
tx_data <= 0;
tx_load_strobe <= 0;
tx_start_strobe <= 0;
tx_complete <= 0;
rx_reset <= 0;
rx_read_strobe <= 0;
rx_buffer <= 0;
end
previous_tx_active <= tx_active;
end
assign loopback = control_register[0];
assign tx_parity = control_register[3];
assign rx_parity = control_register[6];
endmodule

View File

@@ -0,0 +1,75 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module dual_clock_spi_device (
input clk_slow,
input clk_fast,
input spi_sck,
input spi_cs_n,
input spi_sdi,
output spi_sdo,
output reg [7:0] rx_data,
output reg rx_strobe,
input [7:0] tx_data,
input tx_strobe
);
wire [7:0] rx_data_fast;
wire rx_strobe_fast;
reg [7:0] tx_data_fast;
wire tx_strobe_fast;
spi_device spi (
.clk(clk_fast),
.spi_sck(spi_sck),
.spi_cs_n(spi_cs_n),
.spi_sdi(spi_sdi),
.spi_sdo(spi_sdo),
.rx_data(rx_data_fast),
.rx_strobe(rx_strobe_fast),
.tx_data(tx_data_fast),
.tx_strobe(tx_strobe_fast)
);
wire rx_strobe_slow;
strobe_cdc rx_strobe_cdc (
.clk_in(clk_fast),
.strobe_in(rx_strobe_fast),
.clk_out(clk_slow),
.strobe_out(rx_strobe_slow)
);
strobe_cdc tx_strobe_cdc (
.clk_in(clk_slow),
.strobe_in(tx_strobe),
.clk_out(clk_fast),
.strobe_out(tx_strobe_fast)
);
always @(posedge clk_slow)
begin
if (tx_strobe)
tx_data_fast <= tx_data;
rx_strobe <= 0;
if (rx_strobe_slow)
begin
rx_data <= rx_data_fast;
rx_strobe <= 1;
end
end
endmodule

View File

@@ -0,0 +1,20 @@
# See https://github.com/halfmanhalftaco/fpga-docker/tree/master/Lattice_iCEcube2
ICECUBE2_WRAPPER = docker run -it --rm --volume $(RTL):/data --workdir /data --mac-address=$(shell cat .mac_address) icecube2:latest ./icecube2_env.sh
RTL = $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
IMPLMNT = coax_Implmnt
all: top.bin
$(IMPLMNT)/coax.edf: coax_syn.prj *.v third_party/*.v clocks.sdc
$(ICECUBE2_WRAPPER) synpwrap -prj coax_syn.prj
top.bin: $(IMPLMNT)/coax.edf pins.pcf
rm -f top.bin
$(ICECUBE2_WRAPPER) ./icecube2_flow.tcl
cp $(IMPLMNT)/sbt/outputs/bitmap/top_bitmap.bin top.bin
clean:
rm -rf $(IMPLMNT) synlog.tcl *.bin *.log *.log.bak
.PHONY: all clean

View File

@@ -0,0 +1,15 @@
#!/bin/sh
export ICECUBE_DIR="/opt/icecube2"
export SBT_DIR="$ICECUBE_DIR/sbt_backend"
export FOUNDRY="$ICECUBE_DIR/LSE"
export SYNPLIFY_PATH="$ICECUBE_DIR/synpbase"
export LM_LICENSE_FILE="$ICECUBE_DIR/license.dat"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}$SBT_DIR/bin/linux/opt:$SBT_DIR/bin/linux/opt/synpwrap:$SBT_DIR/lib/linux/opt:$FOUNDRY/bin/lin64"
# Give precedence to iCEcube2 tools.
export PATH="$SBT_DIR/bin/linux/opt:$SBT_DIR/bin/linux/opt/synpwrap${PATH:+:}$PATH"
$*

View File

@@ -0,0 +1,6 @@
#!/usr/bin/tclsh8.5
# Place and route, bitmap generation and timing
source [file join $::env(SBT_DIR) "tcl/sbt_backend_synpl.tcl"]
run_sbt_backend_auto iCE40UP5K-SG48 top [pwd] coax_Implmnt ":edifparser -y pins.pcf" coax

View File

@@ -0,0 +1,25 @@
set_io clk 37
set_io reset_n 20
# SPI
set_io spi_sck 15
set_io spi_cs_n 16
set_io spi_sdi 17
set_io spi_sdo 14
# TX
set_io tx_active 35
set_io tx_n 36
set_io tx_delay 34
# RX
set_io rx 26
set_io irq 13
set_io gpio0 46
set_io gpio1 47
set_io gpio2 48
set_io gpio3 6

View File

@@ -0,0 +1,79 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module spi_device (
input clk,
input spi_sck,
input spi_cs_n,
input spi_sdi,
output spi_sdo,
output reg [7:0] rx_data,
output reg rx_strobe,
input [7:0] tx_data,
input tx_strobe
);
reg [1:0] cs_n_d;
reg [2:0] sck_d;
reg [2:0] sdi_d;
always @(posedge clk)
begin
cs_n_d <= { cs_n_d[0], spi_cs_n };
sck_d <= { sck_d[1:0], spi_sck };
sdi_d <= { sdi_d[1:0], spi_sdi };
end
reg [3:0] counter;
reg [7:0] input_data;
reg [7:0] output_data;
always @(posedge clk)
begin
rx_strobe <= 0;
if (tx_strobe)
output_data <= tx_data;
if (cs_n_d[1])
begin
counter <= 0;
end
else
begin
if (!sck_d[2] && sck_d[1])
begin
input_data <= { input_data[6:0], sdi_d[2] };
counter <= counter + 1;
end
if (sck_d[2] && !sck_d[1])
begin
output_data <= { output_data[6:0], 1'b0 };
if (counter == 8)
begin
rx_data <= input_data;
rx_strobe <= 1;
counter <= 0;
end
end
end
end
assign spi_sdo = output_data[7];
endmodule

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module strobe_cdc (
input clk_in,
input strobe_in,
input clk_out,
output reg strobe_out
);
reg toggle_in;
always @(posedge clk_in)
begin
if (strobe_in)
toggle_in <= ~toggle_in;
end
reg [2:0] toggle_out;
always @(posedge clk_out)
begin
toggle_out <= { toggle_out[1:0], toggle_in };
strobe_out <= 0;
if (toggle_out[2] != toggle_out[1])
strobe_out <= 1;
end
endmodule

View File

@@ -0,0 +1,161 @@
// https://raw.githubusercontent.com/smunaut/ice40-playground/68ac87f6c458712a41d5b8e305d222849233ff00/cores/misc/rtl/fifo_sync_ram.v
/*
* fifo_sync_ram.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2019 Sylvain Munaut <tnt@246tNt.com>
* All rights reserved.
*
* BSD 3-clause, see LICENSE.bsd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
`default_nettype none
module fifo_sync_ram #(
parameter integer DEPTH = 256,
parameter integer WIDTH = 16
)(
input wire [WIDTH-1:0] wr_data,
input wire wr_ena,
output wire wr_full,
output wire [WIDTH-1:0] rd_data,
input wire rd_ena,
output wire rd_empty,
input wire clk,
input wire rst
);
localparam AWIDTH = $clog2(DEPTH);
// Signals
// -------
// RAM
reg [AWIDTH-1:0] ram_wr_addr;
wire [ WIDTH-1:0] ram_wr_data;
wire ram_wr_ena;
reg [AWIDTH-1:0] ram_rd_addr;
wire [ WIDTH-1:0] ram_rd_data;
wire ram_rd_ena;
// Fill-level
reg [AWIDTH:0] level;
(* keep="true" *) wire lvl_dec;
(* keep="true" *) wire lvl_mov;
wire lvl_empty;
// Full
wire full_nxt;
reg full;
// Read logic
reg rd_valid;
// Fill level counter
// ------------------
// (counts the number of used words - 1)
always @(posedge clk or posedge rst)
if (rst)
level <= {(AWIDTH+1){1'b1}};
else
level <= level + { {AWIDTH{lvl_dec}}, lvl_mov };
assign lvl_dec = ram_rd_ena & ~ram_wr_ena;
assign lvl_mov = ram_rd_ena ^ ram_wr_ena;
assign lvl_empty = level[AWIDTH];
// Full flag generation
// --------------------
assign full_nxt = level == { 1'b0, {(AWIDTH-2){1'b1}}, 2'b01 };
always @(posedge clk or posedge rst)
if (rst)
full <= 1'b0;
else
full <= (full | (wr_ena & ~rd_ena & full_nxt)) & ~(rd_ena & ~wr_ena);
assign wr_full = full;
// Write
// -----
always @(posedge clk or posedge rst)
if (rst)
ram_wr_addr <= 0;
else if (ram_wr_ena)
ram_wr_addr <= ram_wr_addr + 1;
assign ram_wr_data = wr_data;
assign ram_wr_ena = wr_ena;
// Read
// ----
always @(posedge clk or posedge rst)
if (rst)
ram_rd_addr <= 0;
else if (ram_rd_ena)
ram_rd_addr <= ram_rd_addr + 1;
assign ram_rd_ena = (rd_ena | ~rd_valid) & ~lvl_empty;
always @(posedge clk or posedge rst)
if (rst)
rd_valid <= 1'b0;
else if (rd_ena | ~rd_valid)
rd_valid <= ~lvl_empty;
assign rd_data = ram_rd_data;
assign rd_empty = ~rd_valid;
// RAM
// ---
ram_sdp #(
.AWIDTH(AWIDTH),
.DWIDTH(WIDTH)
) ram_I (
.wr_addr(ram_wr_addr),
.wr_data(ram_wr_data),
.wr_ena(ram_wr_ena),
.rd_addr(ram_rd_addr),
.rd_data(ram_rd_data),
.rd_ena(ram_rd_ena),
.clk(clk)
);
endmodule // fifo_sync_ram

View File

@@ -0,0 +1,73 @@
// https://raw.githubusercontent.com/smunaut/ice40-playground/68ac87f6c458712a41d5b8e305d222849233ff00/cores/misc/rtl/ram_sdp.v
/*
* ram_sdp.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2019 Sylvain Munaut <tnt@246tNt.com>
* All rights reserved.
*
* BSD 3-clause, see LICENSE.bsd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
`default_nettype none
module ram_sdp #(
parameter integer AWIDTH = 9,
parameter integer DWIDTH = 8
)(
input wire [AWIDTH-1:0] wr_addr,
input wire [DWIDTH-1:0] wr_data,
input wire wr_ena,
input wire [AWIDTH-1:0] rd_addr,
output reg [DWIDTH-1:0] rd_data,
input wire rd_ena,
input wire clk
);
// Signals
reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
`ifdef SIM
integer i;
initial
for (i=0; i<(1<<AWIDTH); i=i+1)
ram[i] = 0;
`endif
always @(posedge clk)
begin
// Read
if (rd_ena)
rd_data <= ram[rd_addr];
// Write
if (wr_ena)
ram[wr_addr] <= wr_data;
end
endmodule // ram_sdp

201
interface2/fpga/rtl/top.v Normal file
View File

@@ -0,0 +1,201 @@
// Copyright (c) 2020, Andrew Kay
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
`default_nettype none
module top (
input clk,
input reset_n,
// SPI
input spi_sck,
input spi_cs_n,
input spi_sdi,
output spi_sdo,
// TX
output tx_active,
output tx_n,
output tx_delay,
// RX
input rx,
output irq,
output gpio0,
output gpio1,
output gpio2,
output gpio3
);
reg [1:0] reset_n_d;
reg [1:0] rx_d;
always @(posedge clk)
begin
reset_n_d <= { reset_n_d[0], reset_n };
rx_d <= { rx_d[0], rx };
end
reg reset;
always @(posedge clk)
begin
reset <= !reset_n_d[1];
end
wire clk_fast;
SB_PLL40_CORE #(
.FEEDBACK_PATH("SIMPLE"),
.DIVR(4'b0000),
.DIVF(7'b0010000),
.DIVQ(3'b011),
.FILTER_RANGE(3'b011)
) clk_fast_pll (
.RESETB(1'b1),
.BYPASS(1'b0),
.REFERENCECLK(clk),
.PLLOUTCORE(clk_fast)
);
wire [7:0] spi_rx_data;
wire spi_rx_strobe;
wire [7:0] spi_tx_data;
wire spi_tx_strobe;
dual_clock_spi_device spi (
.clk_slow(clk),
.clk_fast(clk_fast),
.spi_sck(spi_sck),
.spi_cs_n(spi_cs_n),
.spi_sdi(spi_sdi),
.spi_sdo(spi_sdo),
.rx_data(spi_rx_data),
.rx_strobe(spi_rx_strobe),
.tx_data(spi_tx_data),
.tx_strobe(spi_tx_strobe)
);
wire loopback;
wire tx_reset;
wire internal_tx_active;
wire internal_tx;
wire [9:0] tx_data;
wire tx_load_strobe;
wire tx_start_strobe;
wire tx_empty;
wire tx_full;
wire tx_ready;
wire tx_parity;
coax_buffered_tx #(
.CLOCKS_PER_BIT(16),
.DEPTH(2048),
.START_DEPTH(1536)
) coax_buffered_tx (
.clk(clk),
.reset(reset || tx_reset),
.active(internal_tx_active),
.tx(internal_tx),
.data(tx_data),
.load_strobe(tx_load_strobe),
.start_strobe(tx_start_strobe),
.empty(tx_empty),
.full(tx_full),
.ready(tx_ready),
.parity(tx_parity)
);
reg internal_rx;
always @(posedge clk)
begin
internal_rx <= loopback ? internal_tx : (!internal_tx_active ? rx_d[1] : 0);
end
wire rx_reset;
wire rx_active;
wire rx_error;
wire [9:0] rx_data;
wire rx_read_strobe;
wire rx_empty;
wire rx_parity;
coax_buffered_rx #(
.CLOCKS_PER_BIT(16),
.DEPTH(2048)
) coax_buffered_rx (
.clk(clk),
.reset(reset || rx_reset),
.rx(internal_rx),
.active(rx_active),
.error(rx_error),
.data(rx_data),
.read_strobe(rx_read_strobe),
.empty(rx_empty),
.parity(rx_parity)
);
control control (
.clk(clk),
.reset(reset),
.spi_cs_n(spi_cs_n),
.spi_rx_data(spi_rx_data),
.spi_rx_strobe(spi_rx_strobe),
.spi_tx_data(spi_tx_data),
.spi_tx_strobe(spi_tx_strobe),
.loopback(loopback),
.tx_reset(tx_reset),
.tx_active(internal_tx_active),
.tx_data(tx_data),
.tx_load_strobe(tx_load_strobe),
.tx_start_strobe(tx_start_strobe),
.tx_empty(tx_empty),
.tx_full(tx_full),
.tx_ready(tx_ready),
.tx_parity(tx_parity),
.rx_reset(rx_reset),
.rx_active(rx_active),
.rx_error(rx_error),
.rx_data(rx_data),
.rx_read_strobe(rx_read_strobe),
.rx_empty(rx_empty),
.rx_parity(rx_parity)
);
coax_tx_distorter #(
.CLOCKS_PER_BIT(16)
) coax_tx_distorter (
.clk(clk),
.active_input(!loopback && internal_tx_active),
.tx_input(internal_tx),
.active_output(tx_active),
.tx_delay(tx_delay),
.tx_n(tx_n)
);
assign irq = rx_active || rx_error;
assign gpio0 = rx_d[1];
assign gpio1 = tx_active;
assign gpio2 = rx_active;
assign gpio3 = 0;
endmodule