summaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/control')
-rw-r--r--fpga/usrp3/lib/control/Makefile.srcs26
-rw-r--r--fpga/usrp3/lib/control/README.txt0
-rw-r--r--fpga/usrp3/lib/control/arb_qualify_master.v88
-rw-r--r--fpga/usrp3/lib/control/axi_crossbar.v167
-rw-r--r--fpga/usrp3/lib/control/axi_crossbar_tb.v214
-rw-r--r--fpga/usrp3/lib/control/axi_fifo_header.v84
-rw-r--r--fpga/usrp3/lib/control/axi_forwarding_cam.v232
-rw-r--r--fpga/usrp3/lib/control/axi_slave_mux.v122
-rw-r--r--fpga/usrp3/lib/control/axi_test_vfifo.v139
-rw-r--r--fpga/usrp3/lib/control/cvita_uart.v164
-rw-r--r--fpga/usrp3/lib/control/dram_2port.v27
-rw-r--r--fpga/usrp3/lib/control/gpio_atr.v64
-rw-r--r--fpga/usrp3/lib/control/por_gen.v25
-rw-r--r--fpga/usrp3/lib/control/radio_ctrl_proc.v143
-rw-r--r--fpga/usrp3/lib/control/radio_ctrl_proc_tb.v104
-rw-r--r--fpga/usrp3/lib/control/ram_2port.v49
-rw-r--r--fpga/usrp3/lib/control/reset_sync.v28
-rw-r--r--fpga/usrp3/lib/control/setting_reg.v35
-rw-r--r--fpga/usrp3/lib/control/settings_bus_crossclock.v26
-rw-r--r--fpga/usrp3/lib/control/simple_i2c_core.v104
-rw-r--r--fpga/usrp3/lib/control/simple_spi_core.v207
21 files changed, 2048 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/Makefile.srcs b/fpga/usrp3/lib/control/Makefile.srcs
new file mode 100644
index 000000000..e0ad8a942
--- /dev/null
+++ b/fpga/usrp3/lib/control/Makefile.srcs
@@ -0,0 +1,26 @@
+#
+# Copyright 2013 Ettus Research LLC
+#
+
+##################################################
+# Control Lib Sources
+##################################################
+CONTROL_LIB_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control/, \
+reset_sync.v \
+por_gen.v \
+gpio_atr.v \
+simple_spi_core.v \
+simple_i2c_core.v \
+setting_reg.v \
+settings_bus_crossclock.v \
+radio_ctrl_proc.v \
+ram_2port.v \
+axi_crossbar.v \
+axi_slave_mux.v \
+axi_fifo_header.v \
+arb_qualify_master.v \
+axi_forwarding_cam.v \
+axi_test_vfifo.v \
+dram_2port.v \
+cvita_uart.v \
+))
diff --git a/fpga/usrp3/lib/control/README.txt b/fpga/usrp3/lib/control/README.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/fpga/usrp3/lib/control/README.txt
diff --git a/fpga/usrp3/lib/control/arb_qualify_master.v b/fpga/usrp3/lib/control/arb_qualify_master.v
new file mode 100644
index 000000000..df17fac57
--- /dev/null
+++ b/fpga/usrp3/lib/control/arb_qualify_master.v
@@ -0,0 +1,88 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+
+
+//
+// This module forms the qualification engine for a single master as
+// part of a larger arbitration engine for a slave. It would typically
+// be instantiated from arb_select_master.v to form a complete arbitor solution.
+//
+
+module arb_qualify_master
+ #(
+ parameter WIDTH=16 // Bit width of destination field.
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Header signals
+ input [WIDTH-1:0] header,
+ input header_valid,
+ // Slave Confg Signals
+ input [WIDTH-1:0] slave_addr,
+ input [WIDTH-1:0] slave_mask,
+ input slave_valid,
+ // Arbitration flags
+ output reg master_valid,
+ input master_ack
+ );
+
+ localparam WAIT_HEADER_VALID = 0;
+ localparam MATCH = 1;
+ localparam WAIT_HEADER_NOT_VALID = 2;
+
+
+ reg [1:0] state, next_state;
+
+
+ // Does masked slave address match header field for dest from master?
+ assign header_match = ((header & slave_mask) == (slave_addr & slave_mask)) && slave_valid;
+
+
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= WAIT_HEADER_VALID;
+ master_valid <= 0;
+ end else
+ begin
+ case(state)
+ //
+ // Wait here until Masters FIFO presents a valid header word.
+ //
+ WAIT_HEADER_VALID: begin
+ if (header_valid)
+ if (header_match) begin
+ state <= MATCH;
+ master_valid <= 1;
+ end else
+ next_state <= WAIT_HEADER_NOT_VALID;
+ end
+ //
+ // There should only ever be one match across various arbitors
+ // if they are configured correctly and since the backing FIFO in the
+ // master should not start to drain until the arbitration is won
+ // by that master, master_ack should always preceed de-assertion of
+ // header_valid so we don't check for the other order of deassertion.
+ //
+ MATCH: begin
+ if (master_ack) begin
+ master_valid <= 0;
+ state <= WAIT_HEADER_NOT_VALID;
+ end
+ end
+ //
+ // Wait here until this master starts to drain this packet from his FIFO.
+ //
+ WAIT_HEADER_NOT_VALID: begin
+ if (!header_valid) begin
+ state <= WAIT_HEADER_VALID;
+ end
+ end
+ endcase // case(state)
+ end // else: !if(reset | clear)
+
+endmodule // arb_qualify_master
+
+ \ No newline at end of file
diff --git a/fpga/usrp3/lib/control/axi_crossbar.v b/fpga/usrp3/lib/control/axi_crossbar.v
new file mode 100644
index 000000000..a408f69f0
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_crossbar.v
@@ -0,0 +1,167 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+
+
+`define LOG2(N) (\
+ N < 2 ? 0 : \
+ N < 4 ? 1 : \
+ N < 8 ? 2 : \
+ N < 16 ? 3 : \
+ N < 32 ? 4 : \
+ N < 64 ? 5 : \
+ N < 128 ? 6 : \
+ N < 256 ? 7 : \
+ N < 512 ? 8 : \
+ N < 1024 ? 9 : \
+ 10)
+
+module axi_crossbar
+ #(
+ parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
+ parameter DST_WIDTH = 16, // Width of DST field we are routing on.
+ parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
+ parameter NUM_OUTPUTS = 2 // number of output AXI4-STREAM buses
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ input [7:0] local_addr,
+ // Inputs
+ input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
+ input [NUM_INPUTS-1:0] i_tvalid,
+ input [NUM_INPUTS-1:0] i_tlast,
+ output [NUM_INPUTS-1:0] i_tready,
+ input [NUM_INPUTS-1:0] pkt_present,
+ // Setting Bus
+ input set_stb,
+ input [15:0] set_addr,
+ input [31:0] set_data,
+ // Output
+ output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
+ output [NUM_OUTPUTS-1:0] o_tvalid,
+ output [NUM_OUTPUTS-1:0] o_tlast,
+ input [NUM_OUTPUTS-1:0] o_tready,
+ // readback bus
+ input rb_rd_stb,
+ input [`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:0] rb_addr,
+ output [31:0] rb_data
+ );
+
+ genvar m,n;
+
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_in;
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_in;
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_out;
+ wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_out;
+
+ wire [NUM_INPUTS-1:0] i_tready_slave [0:NUM_OUTPUTS-1];
+
+ //
+ // Instantiate an axi_slave_mux for every slave/output of the Crossbar switch.
+ // Each axi_slave_mux contains logic to maux and resolve arbitration
+ // for this particular slave/output.
+ //
+
+ generate
+ for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: instantiate_slave_mux
+
+ wire [NUM_INPUTS-1:0] i_tready_tmp;
+
+ axi_slave_mux
+ #(
+ .FIFO_WIDTH(FIFO_WIDTH), // AXI4-STREAM data bus width
+ .DST_WIDTH(DST_WIDTH), // Width of DST field we are routing on.
+ .NUM_INPUTS(NUM_INPUTS) // number of input AXI buses
+ ) axi_slave_mux_i
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ // Inputs
+ .i_tdata(i_tdata),
+ .i_tvalid(i_tvalid),
+ .i_tlast(i_tlast),
+ .i_tready(i_tready_tmp),
+ // Forwarding flags (One from each Input/Master)
+ .forward_valid(forward_valid_in[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
+ .forward_ack(forward_ack_out[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
+ // Output
+ .o_tdata(o_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
+ .o_tvalid(o_tvalid[m]),
+ .o_tlast(o_tlast[m]),
+ .o_tready(o_tready[m])
+ );
+
+ if (m==0)
+ assign i_tready_slave[0] = i_tready_tmp;
+ else
+ assign i_tready_slave[m] = i_tready_tmp | i_tready_slave[m-1] ;
+
+ end // block: instantiate_slave_mux
+ endgenerate
+
+ assign i_tready = i_tready_slave[NUM_OUTPUTS-1];
+
+ //
+ // Permute the forwarding flag buses
+ //
+
+ generate
+ for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: permute_outer
+ for (n = 0; n < NUM_INPUTS; n = n + 1) begin: permute_inner
+ assign forward_valid_in[n*NUM_OUTPUTS+m] = forward_valid_out[n+m*NUM_INPUTS];
+ assign forward_ack_in[n+m*NUM_INPUTS] = forward_ack_out[n*NUM_OUTPUTS+m];
+ end
+ end
+
+ endgenerate
+
+
+ //
+ // Instantiate an axi_forwarding_cam for every Input/Master of the Crossbar switch.
+ // Each contains a TCAM like lookup that allocates an egress port.
+ //
+
+ wire [31:0] rb_data_mux[0:NUM_INPUTS-1];
+
+ generate
+ for (m = 0; m < NUM_INPUTS; m = m + 1) begin: instantiate_cam
+ axi_forwarding_cam
+ #(
+ .BASE(0),
+ .WIDTH(FIFO_WIDTH), // Bit width of FIFO word.
+ .NUM_OUTPUTS(NUM_OUTPUTS)
+ ) axi_forwarding_cam_i
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ // Monitored FIFO signals
+ .o_tdata(i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
+ .o_tvalid(i_tvalid[m]),
+ .o_tready(i_tready[m]),
+ .o_tlast(i_tlast[m]),
+ .pkt_present(pkt_present[m]),
+ // Configuration
+ .local_addr(local_addr),
+ // Setting Bus
+ .set_stb(set_stb),
+ .set_addr(set_addr),
+ .set_data(set_data),
+ // Header signals
+ .forward_valid(forward_valid_out[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
+ .forward_ack(forward_ack_in[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
+ // Readback bus
+ .rb_rd_stb(rb_rd_strobe && (rb_addr[`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:`LOG2(NUM_OUTPUTS)] == m)),
+ .rb_addr(rb_addr[`LOG2(NUM_OUTPUTS)-1:0]),
+ .rb_data(rb_data_mux[m])
+ );
+ end // block: instantiate_fifo_header
+ endgenerate
+
+ assign rb_data = rb_data_mux[rb_addr[`LOG2(NUM_OUTPUTS)+`LOG2(NUM_INPUTS)-1:`LOG2(NUM_OUTPUTS)]];
+
+
+endmodule // axi_crossbar
diff --git a/fpga/usrp3/lib/control/axi_crossbar_tb.v b/fpga/usrp3/lib/control/axi_crossbar_tb.v
new file mode 100644
index 000000000..1994cb352
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_crossbar_tb.v
@@ -0,0 +1,214 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+
+`timescale 1 ps / 1 ps
+
+module axi_crossbar_tb;
+
+
+ localparam STREAM_WIDTH = 64;
+
+ // Currently support simulations upto 8x8 configurations
+ localparam MAX_NUM_INPUTS = 8;
+ localparam MAX_NUM_OUTPUTS = 8;
+
+ wire [(MAX_NUM_INPUTS*STREAM_WIDTH)-1:0] i_tdata;
+ wire [STREAM_WIDTH-1:0] i_tdata_array [0:MAX_NUM_INPUTS-1];
+ wire [MAX_NUM_INPUTS-1:0] i_tvalid;
+ wire [MAX_NUM_INPUTS-1:0] i_tready;
+ wire [MAX_NUM_INPUTS-1:0] i_tlast;
+ wire [MAX_NUM_INPUTS-1:0] pkt_present;
+
+ reg [STREAM_WIDTH-1:0] data_in [0:MAX_NUM_INPUTS-1];
+ reg [MAX_NUM_INPUTS-1:0] valid_in;
+ wire [MAX_NUM_INPUTS-1:0] ready_in;
+ reg [MAX_NUM_INPUTS-1:0] last_in;
+
+ wire [(MAX_NUM_OUTPUTS*STREAM_WIDTH)-1:0] o_tdata;
+ wire [STREAM_WIDTH-1:0] o_tdata_array [0:MAX_NUM_OUTPUTS-1];
+ wire [MAX_NUM_OUTPUTS-1:0] o_tvalid;
+ wire [MAX_NUM_OUTPUTS-1:0] o_tready;
+ wire [MAX_NUM_OUTPUTS-1:0] o_tlast;
+
+
+ wire [STREAM_WIDTH-1:0] data_out [0:MAX_NUM_OUTPUTS-1];
+ wire [MAX_NUM_OUTPUTS-1:0] valid_out;
+ reg [MAX_NUM_OUTPUTS-1:0] ready_out;
+ wire [MAX_NUM_OUTPUTS-1:0] last_out;
+
+
+ genvar m;
+
+ reg clk;
+ reg reset;
+ reg clear;
+ reg set_stb;
+ reg [15:0] set_addr;
+ reg [31:0] set_data;
+
+ // reg reset;
+
+ //
+ // Simulation specific testbench is included here
+ //
+`include "task_library.v"
+`include "simulation_script.v"
+
+
+ //
+ // Define Clocks
+ //
+ initial begin
+ clk = 1'b1;
+ end
+
+ // 125MHz clock
+ always #4000 clk = ~clk;
+
+ //
+ // Good starting state
+ //
+ initial begin
+ reset <= 0;
+ clear <= 0;
+ set_stb <= 0;
+ set_addr <= 0;
+ set_data <= 0;
+/* -----\/----- EXCLUDED -----\/-----
+ data_in[0] <= 0;
+ valid_in[0] <= 0;
+ last_in[0] <= 0;
+
+ data_in[1] <= 0;
+ valid_in[1] <= 0;
+ last_in[1] <= 0;
+ -----/\----- EXCLUDED -----/\----- */
+
+
+ end
+
+
+
+ //
+ // AXI Crossbar instance
+ //
+ localparam SR_AWIDTH = 16;
+ localparam SR_XB_LOCAL = 512;
+
+ wire [7:0] local_addr;
+
+ setting_reg #(.my_addr(SR_XB_LOCAL), .awidth(SR_AWIDTH), .width(8)) sr_local_addr
+ (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(local_addr),.changed());
+
+ axi_crossbar
+ #(
+ .FIFO_WIDTH(STREAM_WIDTH), // AXI4-STREAM data bus width
+ .DST_WIDTH(16), // Width of DST field we are routing on.
+ .NUM_INPUTS(NUM_INPUTS), // number of input AXI4-STREAM buses
+ .NUM_OUTPUTS(NUM_OUTPUTS) // number of output AXI4-STREAM buses
+ ) axi_crossbar_i
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ .local_addr(local_addr),
+ // Inputs
+ .i_tdata(i_tdata[(NUM_INPUTS*STREAM_WIDTH)-1:0]),
+ .i_tvalid(i_tvalid[NUM_INPUTS-1:0]),
+ .i_tlast(i_tlast[NUM_INPUTS-1:0]),
+ .i_tready(i_tready[NUM_INPUTS-1:0]),
+ .pkt_present(pkt_present[NUM_INPUTS-1:0]),
+ // Settings bus
+ .set_stb(set_stb),
+ .set_addr(set_addr),
+ .set_data(set_data),
+ // Output
+ .o_tdata(o_tdata[(NUM_OUTPUTS*STREAM_WIDTH)-1:0]),
+ .o_tvalid(o_tvalid[NUM_OUTPUTS-1:0]),
+ .o_tlast(o_tlast[NUM_OUTPUTS-1:0]),
+ .o_tready(o_tready[NUM_OUTPUTS-1:0]),
+ // Readback Bus
+ .rb_rd_stb(1'b0),
+ .rb_addr(0),
+ .rb_data()
+ );
+
+ //
+ // Input FIFOs
+ //
+ generate
+ for (m=0;m<NUM_INPUTS;m=m+1)
+ begin: input_fifos
+
+ assign i_tdata[(STREAM_WIDTH*m)+STREAM_WIDTH-1:STREAM_WIDTH*m] = i_tdata_array[m];
+
+ axi_fifo_short
+ #(.WIDTH(STREAM_WIDTH+1)) axi_fifo_short_in
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ .o_tdata({i_tlast[m],i_tdata_array[m]}),
+ .o_tvalid(i_tvalid[m]),
+ .o_tready(i_tready[m]),
+ .i_tdata({last_in[m],data_in[m]}),
+ .i_tvalid(valid_in[m]),
+ .i_tready(ready_in[m]),
+ .space(),
+ .occupied()
+ );
+
+ monitor_axi_fifo
+ #(
+ .COUNT_BITS(8)
+ ) monitor_axi_fifo_in
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ // Monitored FIFO signals
+ .i_tvalid(valid_in[m]),
+ .i_tready(ready_in[m]),
+ .i_tlast(last_in[m]),
+ .o_tvalid(i_tvalid[m]),
+ .o_tready(i_tready[m]),
+ .o_tlast(i_tlast[m]),
+ // FIFO status output
+ .pkt_present(pkt_present[m]), // Flags any whole packets present
+ .pkt_count()
+ );
+
+ end
+ endgenerate
+
+
+ //
+ // Output FIFO's
+ //
+ generate
+ for (m=0;m<NUM_OUTPUTS;m=m+1)
+ begin: output_fifos
+
+ assign o_tdata_array[m] = o_tdata[(STREAM_WIDTH*m)+STREAM_WIDTH-1:STREAM_WIDTH*m];
+
+ axi_fifo_short
+ #(.WIDTH(STREAM_WIDTH+1)) axi_fifo_short_out
+ (
+ .clk(clk),
+ .reset(reset),
+ .clear(clear),
+ .i_tdata({o_tlast[m],o_tdata_array[m]}),
+ .i_tvalid(o_tvalid[m]),
+ .i_tready(o_tready[m]),
+ .o_tdata({last_out[m],data_out[m]}),
+ .o_tvalid(valid_out[m]),
+ .o_tready(ready_out[m]),
+ .space(),
+ .occupied()
+ );
+ end
+ endgenerate // block: output_fifos
+
+endmodule // axi_crossbar_tb
diff --git a/fpga/usrp3/lib/control/axi_fifo_header.v b/fpga/usrp3/lib/control/axi_fifo_header.v
new file mode 100644
index 000000000..ceac8e324
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_fifo_header.v
@@ -0,0 +1,84 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+
+
+//
+// This module is connected to the output port of an AXI4-STREAM FIFO that is used to move packetized data.
+// It extracts and indicates the header (first word) of a packet in the FIFO. The header and flag are pipelined
+// for timing closure.
+//
+
+module axi_fifo_header
+ #(
+ parameter WIDTH=64 // Bit width of FIFO word.
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Monitored FIFO signals
+ input [WIDTH-1:0] o_tdata,
+ input o_tvalid,
+ input o_tready,
+ input o_tlast,
+ input pkt_present,
+ // Header signals
+ output reg [WIDTH-1:0] header,
+ output reg header_valid
+ );
+
+ localparam WAIT_SOF = 0;
+ localparam WAIT_EOF = 1;
+
+ reg out_state;
+
+
+ //
+ // Monitor packets leaving FIFO
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ out_state <= WAIT_SOF;
+ end else
+ case(out_state)
+ //
+ // After RESET or the EOF of previous packet, the first cycle with
+ // output valid asserted is the SOF and presents the Header word.
+ // The cycle following the concurrent presentation of asserted output
+ // valid and output ready presents the word following the header.
+ //
+ WAIT_SOF:
+ if (o_tvalid && o_tready) begin
+ out_state <= WAIT_EOF;
+ end else begin
+ out_state <= WAIT_SOF;
+ end
+ //
+ // EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
+ //
+ WAIT_EOF:
+ if (o_tlast && o_tvalid && o_tready) begin
+ out_state <= WAIT_SOF;
+ end else begin
+ out_state <= WAIT_EOF;
+ end
+ endcase // case(in_state)
+
+ //
+ // Pipeline Header signals
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ header <= 0;
+ header_valid <= 0;
+ end else if (o_tvalid && (out_state == WAIT_SOF) && pkt_present) begin
+ // Header will remian valid until o_tready is asserted as this will cause a state transition.
+ header <= o_tdata;
+ header_valid <= 1;
+ end else begin
+ header_valid <= 0;
+ end
+
+
+endmodule // axi_fifo_header
diff --git a/fpga/usrp3/lib/control/axi_forwarding_cam.v b/fpga/usrp3/lib/control/axi_forwarding_cam.v
new file mode 100644
index 000000000..2f28b5640
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_forwarding_cam.v
@@ -0,0 +1,232 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+
+
+//
+// This module implements a highly customized TCAM that enbales forwarding
+// decisions to be made on a 16bit field from a VITA SID field.
+// The 16bits are allocated by convention as 8 bits of Network address
+// (Addresses USRP's etc) and 8 bits of Host address (adresses endpoints in
+// a USRP). By definition if the DEST field in the SID addresses a different
+// USRP than this one then we don't care about the Host field, only the Network Field.
+// We only look at the Host Field when the Network field addresses us.
+// Thus Need TCAM of 256+256 entries with Log2(N) bits, where N is the number of
+// slave(output) ports on the crossbar switch.
+//
+//
+//
+// SID format:
+//
+// |--------|---------|--------|---------|
+// | SOURCE | DEST | DEST |
+// | ADDRESS | NETWORK| HOST |
+// |--------|---------|--------|---------|
+// 8 8 8 8
+//
+
+
+`define LOG2(N) (\
+ N < 2 ? 0 : \
+ N < 4 ? 1 : \
+ N < 8 ? 2 : \
+ N < 16 ? 3 : \
+ N < 32 ? 4 : \
+ N < 64 ? 5 : \
+ N < 128 ? 6 : \
+ N < 256 ? 7 : \
+ N < 512 ? 8 : \
+ N < 1024 ? 9 : \
+ 10)
+
+module axi_forwarding_cam
+ #(
+ parameter BASE = 0, // BASE address for setting registers in this block. (512 addrs used)
+ parameter WIDTH=64, // Bit width of FIFO word.
+ parameter NUM_OUTPUTS=2 // Number of outputs (destinations) in crossbar.
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Monitored FIFO signals
+ input [WIDTH-1:0] o_tdata,
+ input o_tvalid,
+ input o_tready,
+ input o_tlast,
+ input pkt_present,
+ // Configuration
+ input [7:0] local_addr,
+ // Setting Bus
+ input set_stb,
+ input [15:0] set_addr,
+ input [31:0] set_data,
+ // Forwarding Flags
+ output reg [NUM_OUTPUTS-1:0] forward_valid,
+ input [NUM_OUTPUTS-1:0] forward_ack,
+ // readback bus
+ input rb_rd_stb,
+ input [`LOG2(NUM_OUTPUTS)-1:0] rb_addr,
+ output [31:0] rb_data
+ );
+
+
+ localparam WAIT_SOF = 0;
+ localparam WAIT_EOF = 1;
+ reg state;
+
+ localparam IDLE = 0;
+ localparam FORWARD = 1;
+ localparam WAIT = 2;
+
+ reg [1:0] demux_state;
+
+ reg [15:0] dst;
+ reg dst_valid, dst_valid_reg;
+ wire local_dst;
+ wire [8:0] read_addr;
+
+ //
+ // Monitor packets leaving FIFO
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= WAIT_SOF;
+ end else
+ case(state)
+ //
+ // After RESET or the EOF of previous packet, the first cycle with
+ // output valid asserted is the SOF and presents the Header word.
+ // The cycle following the concurrent presentation of asserted output
+ // valid and output ready presents the word following the header.
+ //
+ WAIT_SOF:
+ if (o_tvalid && o_tready) begin
+ state <= WAIT_EOF;
+ end else begin
+ state <= WAIT_SOF;
+ end
+ //
+ // EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
+ //
+ WAIT_EOF:
+ if (o_tlast && o_tvalid && o_tready) begin
+ state <= WAIT_SOF;
+ end else begin
+ state <= WAIT_EOF;
+ end
+ endcase // case(in_state)
+
+ //
+ // Extract Destination fields(s) from SID
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ dst <= 0;
+ dst_valid <= 0;
+ dst_valid_reg <= 0;
+ end else if (o_tvalid && (state == WAIT_SOF) && pkt_present) begin
+ // SID will remain valid until o_tready is asserted as this will cause a state transition.
+ dst <= o_tdata[15:0];
+ dst_valid <= 1;
+ dst_valid_reg <= dst_valid;
+ end else begin
+ dst_valid <= 0;
+ dst_valid_reg <= dst_valid;
+ end
+
+ //
+ // Is Network field in DST our local address?
+ //
+ assign local_dst = (dst[15:8] == local_addr) && dst_valid;
+
+
+ //
+ // Mux address to RAM so that it searches CAM for Network field or Host field.
+ // Network addresses are stored in the lower 256 locations, host addresses the upper 256.
+ //
+ assign read_addr = {local_dst,(local_dst ? dst[7:0] : dst[15:8])};
+
+ //
+ // Imply a block RAM here, 512xCeil(Log2(NUM_OUTPUTS))
+ //
+ //synthesis attribute ram_style of mem is block
+ reg [(`LOG2(NUM_OUTPUTS))-1 : 0] mem [0:511];
+ reg [8:0] read_addr_reg;
+ wire write;
+ wire [`LOG2(NUM_OUTPUTS)-1:0] read_data;
+
+ assign write = (set_addr[15:9] == (BASE >>9)) && set_stb; // Addr decode.
+
+ always @(posedge clk)
+ begin
+ read_addr_reg <= read_addr;
+
+ if (write) begin
+ mem[set_addr[8:0]] <= set_data[`LOG2(NUM_OUTPUTS)-1:0];
+ end
+
+ end
+
+ assign read_data = mem[read_addr_reg];
+
+
+ //
+ // State machine to manage forwarding flags.
+ //
+ always @(posedge clk)
+ if (reset | clear) begin
+ demux_state <= IDLE;
+ end else
+ case(demux_state)
+
+ // Wait for Valid DST which indicates a new packet lookup in the CAM.
+ IDLE: begin
+ if (dst_valid_reg == 1) begin
+ forward_valid <= 1 << read_data;
+ demux_state <= FORWARD;
+ end
+ end
+ // When Slave/Output thats forwarding ACK's the forward flag, clear request and wait for packet to be transfered
+ FORWARD: begin
+ if ((forward_ack & forward_valid) != 0) begin
+ forward_valid <= 0;
+ demux_state <= WAIT;
+ end
+ end
+ // When packet transfered go back to idle.
+ WAIT: begin
+ if (forward_ack == 0)
+ demux_state <= IDLE;
+ end
+
+ endcase // case (demux_state)
+
+ //
+ // Compile forwarding statistics
+ // (This uses a lot of registers!)
+ //
+ genvar m;
+ reg [31:0] statistics [0:NUM_OUTPUTS-1];
+
+ generate
+ for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: generate_stats
+ always @(posedge clk)
+ if (reset | clear)
+ statistics[m] <= 0;
+ else if ((rb_addr == m) && rb_rd_stb)
+ statistics[m] <= 0;
+ else if (forward_ack[m] & forward_valid[m])
+ statistics[m] <= statistics[m] + 1;
+ end
+ endgenerate
+
+ assign rb_data = statistics[rb_addr];
+
+
+endmodule
+
+
+
+
+
diff --git a/fpga/usrp3/lib/control/axi_slave_mux.v b/fpga/usrp3/lib/control/axi_slave_mux.v
new file mode 100644
index 000000000..1a307aba5
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_slave_mux.v
@@ -0,0 +1,122 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+
+
+
+
+`define LOG2(N) (\
+ N < 2 ? 0 : \
+ N < 4 ? 1 : \
+ N < 8 ? 2 : \
+ N < 16 ? 3 : \
+ N < 32 ? 4 : \
+ N < 64 ? 5 : \
+ N < 128 ? 6 : \
+ N < 256 ? 7 : \
+ N < 512 ? 8 : \
+ N < 1024 ? 9 : \
+ 10)
+
+
+module axi_slave_mux
+ #(
+ parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
+ parameter DST_WIDTH = 16, // Width of DST field we are routing on.
+ parameter NUM_INPUTS = 2 // number of input AXI buses
+ )
+ (
+ input clk,
+ input reset,
+ input clear,
+ // Inputs
+ input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
+ input [NUM_INPUTS-1:0] i_tvalid,
+ input [NUM_INPUTS-1:0] i_tlast,
+ output [NUM_INPUTS-1:0] i_tready,
+ // Forwarding Flags
+ input [NUM_INPUTS-1:0] forward_valid,
+ output reg [NUM_INPUTS-1:0] forward_ack,
+ // Output
+ output [FIFO_WIDTH-1:0] o_tdata,
+ output o_tvalid,
+ output o_tlast,
+ input o_tready
+ );
+
+ wire [FIFO_WIDTH-1:0] i_tdata_array [0:NUM_INPUTS-1];
+
+ reg [`LOG2(NUM_INPUTS):0] select;
+ reg enable;
+
+
+ reg state;
+
+ localparam CHECK_THIS_INPUT = 0;
+ localparam WAIT_LAST = 1;
+
+
+ always @(posedge clk)
+ if (reset | clear) begin
+ state <= CHECK_THIS_INPUT;
+ select <= 0;
+ enable <= 0;
+ forward_ack <= 0;
+ end else begin
+ case(state)
+ // Is the currently selected input addressing this slave with a ready packet?
+ CHECK_THIS_INPUT: begin
+ if (forward_valid[select]) begin
+ enable <= 1;
+ forward_ack[select] <= 1;
+ state <= WAIT_LAST;
+ end else if (select == NUM_INPUTS - 1 ) begin
+ select <= 0;
+ end else begin
+ select <= select + 1;
+ end
+ end
+ // Assert ACK immediately to forwarding logic and then wait for end of packet.
+ WAIT_LAST: begin
+
+ if (i_tlast[select] && i_tvalid[select] && o_tready) begin
+ if (select == NUM_INPUTS - 1 ) begin
+ select <= 0;
+ end else begin
+ select <= select + 1;
+ end
+ state <= CHECK_THIS_INPUT;
+ forward_ack <= 0;
+ enable <= 0;
+ end else begin
+ forward_ack[select] <= 1;
+ enable <= 1;
+ end
+ end
+ endcase // case(state)
+ end
+
+ //
+ // Combinatorial mux
+ //
+ genvar m;
+
+ generate
+ for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_buses
+ assign i_tdata_array[m] = i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH];
+ end
+ endgenerate
+
+ assign o_tdata = i_tdata_array[select];
+ assign o_tvalid = enable && i_tvalid[select];
+ assign o_tlast = enable && i_tlast[select];
+ // assign i_tready = {NUM_INPUTS{o_tready}} & (enable << select);
+
+ generate
+ for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_ready
+ assign i_tready[m] = o_tready && enable && (select == m);
+ end
+ endgenerate
+
+
+endmodule // axi_slave_mux
diff --git a/fpga/usrp3/lib/control/axi_test_vfifo.v b/fpga/usrp3/lib/control/axi_test_vfifo.v
new file mode 100644
index 000000000..a436e9c55
--- /dev/null
+++ b/fpga/usrp3/lib/control/axi_test_vfifo.v
@@ -0,0 +1,139 @@
+//
+// Test Virtual FIFO's by streaming modulo 2^32 counter (replicated in upper
+// and lower 32bits). Test result by tracking count on receive and using
+// sticky flag for error indication.
+// Also provide signal from MSB of 32bit count to blink LED.
+//
+
+module axi_test_vfifo
+ #(parameter PACKET_SIZE = 128)
+ (
+ input aclk,
+ input aresetn,
+ input enable,
+ // AXI Stream Out
+ output reg out_axis_tvalid,
+ input out_axis_tready,
+ output [63 : 0] out_axis_tdata,
+ output reg [7 : 0] out_axis_tstrb,
+ output reg [7 : 0] out_axis_tkeep,
+ output reg out_axis_tlast,
+ output reg [0 : 0] out_axis_tid,
+ output reg [0 : 0] out_axis_tdest,
+ input vfifo_full,
+ // AXI Stream In
+ input in_axis_tvalid,
+ output reg in_axis_tready,
+ input [63 : 0] in_axis_tdata,
+ input [7 : 0] in_axis_tstrb,
+ input [7 : 0] in_axis_tkeep,
+ input in_axis_tlast,
+ input [0 : 0] in_axis_tid,
+ input [0 : 0] in_axis_tdest,
+ // Flags
+ output reg flag_error,
+ output heartbeat_in,
+ output heartbeat_out,
+ output [31:0] expected_count
+ );
+
+
+ reg [31:0] out_count;
+ reg [31:0] in_count;
+ reg [63:0] in_axis_tdata_reg;
+ reg in_data_valid;
+
+
+
+ //
+ // Output
+ //
+ always @(posedge aclk)
+ if (!aresetn) begin
+ out_count <= 0;
+ out_axis_tvalid <= 0;
+ out_axis_tid <= 0; // Don't care.
+ out_axis_tdest <= 0; // Only use port 0 of VFIFO.
+ out_axis_tstrb <= 0; // Unused in VFIFO
+ out_axis_tkeep <= 8'hFF; // Always use every byte of data
+ out_axis_tlast <= 1'b0;
+ end else if (enable) begin
+ if (~vfifo_full) begin
+ // Always ready to output new count value.
+ out_axis_tvalid <= 1;
+ if (out_axis_tready)
+ out_count <= out_count + 1;
+ // Assert TLAST every PACKET_SIZE beats.
+ if (out_count[15:0] == PACKET_SIZE)
+ out_axis_tlast <= 1'b1;
+ else
+ out_axis_tlast <= 1'b0;
+ end else begin
+ out_axis_tvalid <= 0;
+ end
+ end else begin
+ out_axis_tlast <= 1'b0;
+ out_axis_tvalid <= 0;
+ end
+
+ assign out_axis_tdata = {out_count,out_count};
+
+ assign heartbeat_out = out_count[28];
+
+
+ //
+ // Input (Ignore TLAST signal)
+ //
+ always @(posedge aclk)
+ if (!aresetn) begin
+ in_axis_tready <= 0;
+ in_axis_tdata_reg <= 0;
+ in_data_valid <= 0;
+
+ end else if (enable) begin
+ in_axis_tready <= 1;
+ in_axis_tdata_reg <= in_axis_tdata;
+ if (in_axis_tvalid)
+ in_data_valid <= 1;
+ else
+ in_data_valid <= 0;
+ end else begin
+ in_data_valid <= 0;
+ in_axis_tready <= 0;
+ end // else: !if(enable)
+
+
+ assign heartbeat_in = in_count[28];
+
+ //
+ // Input Checker
+ //
+ always @(posedge aclk)
+ if (!aresetn) begin
+ in_count <= 0;
+ flag_error <= 0;
+ end else if (enable) begin
+ if (in_data_valid) begin
+
+ if ((in_axis_tdata_reg[63:32] != in_count) || (in_axis_tdata_reg[31:0] != in_count))
+ begin
+ flag_error <= 1;
+ in_count <= in_axis_tdata_reg[63:32] + 1;
+ end
+ else
+ begin
+ flag_error <= 0;
+ in_count <= in_count + 1;
+ end
+
+ end
+ end
+
+ assign expected_count = in_count;
+
+
+endmodule // axi_test_vfifo
+
+
+
+
diff --git a/fpga/usrp3/lib/control/cvita_uart.v b/fpga/usrp3/lib/control/cvita_uart.v
new file mode 100644
index 000000000..cbb272fc2
--- /dev/null
+++ b/fpga/usrp3/lib/control/cvita_uart.v
@@ -0,0 +1,164 @@
+
+//
+// Copyright 2013 Ettus Research LLC
+//
+
+
+//create a compressed vita based uart data interface
+
+module cvita_uart
+#(
+ parameter SIZE = 0
+)
+(
+ //clocking interface
+ input clk, input rst,
+
+ //uart interface
+ input rxd, output txd,
+
+ //chdr fifo input
+ input [63:0] i_tdata,
+ input i_tlast,
+ input i_tvalid,
+ output i_tready,
+
+ //chdr fifo output
+ output [63:0] o_tdata,
+ output o_tlast,
+ output o_tvalid,
+ input o_tready
+);
+
+ reg [31:0] sid;
+
+ //baud clock divider
+ reg [15:0] clkdiv;
+
+ //hold rx in disable until a tx event
+ reg rxd_enable;
+
+ //==================================================================
+ //== RXD capture and packet generation interface
+ //==================================================================
+ wire [7:0] rx_char;
+ wire fifo_empty;
+ wire fifo_read;
+ reg [11:0] seqnum;
+ wire pgen_trigger;
+ wire pgen_done;
+
+ //rx uart capture
+ simple_uart_rx #(.SIZE(SIZE)) simple_uart_rx
+ (
+ .clk(clk), .rst(rst),
+ .fifo_out(rx_char), .fifo_read(fifo_read), .fifo_level(), .fifo_empty(fifo_empty),
+ .clkdiv(clkdiv), .rx(rxd)
+ );
+
+ //packet generation - holds rx character
+ context_packet_gen context_packet_gen
+ (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .trigger(pgen_trigger),
+ .seqnum(seqnum),
+ .sid({sid[15:0], sid[31:16]}),
+ .body({56'b0, rx_char}),
+ .vita_time(64'b0),
+
+ .done(pgen_done),
+ .o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)
+ );
+
+ //state machine to manage pgen and rx uart
+ reg [1:0] rxd_state;
+ localparam RXD_STATE_RECV_CHAR = 0;
+ localparam RXD_STATE_PGEN_TRIG = 1;
+ localparam RXD_STATE_WAIT_DONE = 2;
+ localparam RXD_STATE_READ_FIFO = 3;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ seqnum <= 12'b0;
+ rxd_state <= RXD_STATE_RECV_CHAR;
+ end
+ else case (rxd_state)
+
+ RXD_STATE_RECV_CHAR: begin
+ if (!fifo_empty && rxd_enable) rxd_state <= RXD_STATE_PGEN_TRIG;
+ end
+
+ RXD_STATE_PGEN_TRIG: begin
+ rxd_state <= RXD_STATE_WAIT_DONE;
+ end
+
+ RXD_STATE_WAIT_DONE: begin
+ if (pgen_done) rxd_state <= RXD_STATE_READ_FIFO;
+ end
+
+ RXD_STATE_READ_FIFO: begin
+ rxd_state <= RXD_STATE_RECV_CHAR;
+ seqnum <= seqnum + 1'b1;
+ end
+
+ endcase //rxd_state
+ end
+
+ assign fifo_read = (rxd_state == RXD_STATE_READ_FIFO) || (!rxd_enable);
+ assign pgen_trigger = (rxd_state == RXD_STATE_PGEN_TRIG);
+
+ //==================================================================
+ //== TXD generation and packet control interface
+ //==================================================================
+ wire [7:0] tx_char;
+ wire fifo_write;
+ wire fifo_full;
+
+ simple_uart_tx #(.SIZE(SIZE)) simple_uart_tx
+ (
+ .clk(clk), .rst(rst),
+ .fifo_in(tx_char), .fifo_write(fifo_write), .fifo_level(), .fifo_full(fifo_full),
+ .clkdiv(clkdiv), .baudclk(), .tx(txd)
+ );
+
+ //state machine to manage control and tx uart
+ reg [1:0] txd_state;
+ localparam TXD_STATE_RECV_CHDR = 0;
+ localparam TXD_STATE_RECV_TIME = 1;
+ localparam TXD_STATE_RECV_BODY = 2;
+ localparam TXD_STATE_DROP_FIFO = 3;
+
+ always @(posedge clk) begin
+ if (rst) begin;
+ txd_state <= TXD_STATE_RECV_CHDR;
+ rxd_enable <= 1'b0;
+ end
+ if (i_tvalid && i_tready) case (txd_state)
+
+ TXD_STATE_RECV_CHDR: begin
+ txd_state <= (i_tdata[61])? TXD_STATE_RECV_TIME : TXD_STATE_RECV_BODY;
+ sid <= i_tdata[31:0];
+ end
+
+ TXD_STATE_RECV_TIME: begin
+ txd_state <= TXD_STATE_RECV_BODY;
+ end
+
+ TXD_STATE_RECV_BODY: begin
+ txd_state <= (i_tlast)? TXD_STATE_RECV_CHDR : TXD_STATE_DROP_FIFO;
+ clkdiv <= i_tdata[47:32];
+ rxd_enable <= 1'b1;
+ end
+
+ TXD_STATE_DROP_FIFO: begin
+ if (i_tlast) txd_state <= TXD_STATE_RECV_CHDR;
+ end
+
+ endcase //txd_state
+ end
+
+ assign tx_char = i_tdata[7:0];
+ assign fifo_write = (txd_state == TXD_STATE_RECV_BODY) && i_tvalid && i_tready;
+ assign i_tready = !fifo_full;
+
+endmodule // cvita_uart
diff --git a/fpga/usrp3/lib/control/dram_2port.v b/fpga/usrp3/lib/control/dram_2port.v
new file mode 100644
index 000000000..186af44e7
--- /dev/null
+++ b/fpga/usrp3/lib/control/dram_2port.v
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////
+// Copyright Ettus Research LLC
+////////////////////////////////////////////////////////////////////////
+
+module dram_2port
+ #(parameter DWIDTH=32,
+ parameter AWIDTH=9)
+ (input clk,
+ input write,
+ input [AWIDTH-1:0] raddr,
+ input [AWIDTH-1:0] waddr,
+ input [DWIDTH-1:0] wdata,
+ output [DWIDTH-1:0] rdata);
+
+ reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
+ integer i;
+ initial
+ for(i=0;i<(1<<AWIDTH);i=i+1)
+ ram[i] <= {DWIDTH{1'b0}};
+
+ assign rdata = ram[raddr];
+
+ always @(posedge clk) begin
+ if (write) ram[waddr] <= wdata;
+ end
+
+endmodule //dram_2port
diff --git a/fpga/usrp3/lib/control/gpio_atr.v b/fpga/usrp3/lib/control/gpio_atr.v
new file mode 100644
index 000000000..9c707e52a
--- /dev/null
+++ b/fpga/usrp3/lib/control/gpio_atr.v
@@ -0,0 +1,64 @@
+
+//
+// Copyright 2011 Ettus Research LLC
+//
+
+
+
+module gpio_atr
+ #(parameter BASE = 0,
+ parameter WIDTH = 32)
+ (input clk, input reset,
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+ input rx, input tx,
+ inout [WIDTH-1:0] gpio,
+ output reg [31:0] gpio_readback
+ );
+
+ wire [WIDTH-1:0] ddr, in_idle, in_tx, in_rx, in_fdx;
+ reg [WIDTH-1:0] rgpio, igpio;
+ reg [WIDTH-1:0] gpio_pipe;
+
+
+ setting_reg #(.my_addr(BASE+0), .width(WIDTH)) reg_idle
+ (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_idle),.changed());
+
+ setting_reg #(.my_addr(BASE+1), .width(WIDTH)) reg_rx
+ (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_rx),.changed());
+
+ setting_reg #(.my_addr(BASE+2), .width(WIDTH)) reg_tx
+ (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_tx),.changed());
+
+ setting_reg #(.my_addr(BASE+3), .width(WIDTH)) reg_fdx
+ (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(in_fdx),.changed());
+
+ setting_reg #(.my_addr(BASE+4), .width(WIDTH)) reg_ddr
+ (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
+ .out(ddr),.changed());
+
+ always @(posedge clk)
+ case({tx,rx})
+ 2'b00: rgpio <= in_idle;
+ 2'b01: rgpio <= in_rx;
+ 2'b10: rgpio <= in_tx;
+ 2'b11: rgpio <= in_fdx;
+ endcase // case ({tx,rx})
+
+ integer n;
+ always @*
+ for(n=0;n<WIDTH;n=n+1)
+ igpio[n] <= ddr[n] ? rgpio[n] : 1'bz;
+
+ assign gpio = igpio;
+
+ // Double pipeline stage for timing, first flop is in IOB, second in core logic.
+ always @(posedge clk) begin
+ gpio_pipe <= gpio;
+ gpio_readback <= gpio_pipe;
+ end
+
+endmodule // gpio_atr
diff --git a/fpga/usrp3/lib/control/por_gen.v b/fpga/usrp3/lib/control/por_gen.v
new file mode 100644
index 000000000..0e4fcd88a
--- /dev/null
+++ b/fpga/usrp3/lib/control/por_gen.v
@@ -0,0 +1,25 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+
+
+
+module por_gen
+ (input clk,
+ output reset_out);
+
+ reg por_rst;
+ reg [7:0] por_counter = 8'h0;
+
+ always @(posedge clk)
+ if (por_counter != 8'h55)
+ begin
+ por_counter <= por_counter + 8'h1;
+ por_rst <= 1'b1;
+ end
+ else
+ por_rst <= 1'b0;
+
+ assign reset_out = por_rst;
+
+endmodule // por_gen
diff --git a/fpga/usrp3/lib/control/radio_ctrl_proc.v b/fpga/usrp3/lib/control/radio_ctrl_proc.v
new file mode 100644
index 000000000..723ba69ff
--- /dev/null
+++ b/fpga/usrp3/lib/control/radio_ctrl_proc.v
@@ -0,0 +1,143 @@
+
+
+// Radio Control Processor
+// Accepts compressed vita extension context packets of the following form:
+// { VITA Compressed Header, Stream ID }
+// { Optional 64 bit time }
+// { 16'h0, setting bus address [15:0], setting [31:0] }
+//
+// If there is a timestamp, packet is held until that time comes.
+// Goes immediately if there is no timestamp or if time has passed.
+// Sends out setting to setting bus, and then generates a response packet
+// with the same sequence number, the src/dest swapped streamid, and the actual time
+// the setting was sent.
+//
+// Note -- if t0 is the requested time, the actual send time on the setting bus is t0 + 1 cycle.
+// Note 2 -- if t1 is the actual time the setting bus, t1+2 is the reported time.
+
+module radio_ctrl_proc
+ (input clk, input reset, input clear,
+
+ input [63:0] ctrl_tdata, input ctrl_tlast, input ctrl_tvalid, output reg ctrl_tready,
+ output reg [63:0] resp_tdata, output reg resp_tlast, output resp_tvalid, input resp_tready,
+
+ input [63:0] vita_time,
+
+ output set_stb, output [7:0] set_addr, output [31:0] set_data,
+ input ready,
+
+ input [63:0] readback,
+
+ output [31:0] debug);
+
+ localparam RC_HEAD = 4'd0;
+ localparam RC_TIME = 4'd1;
+ localparam RC_DATA = 4'd2;
+ localparam RC_DUMP = 4'd3;
+ localparam RC_RESP_HEAD = 4'd4;
+ localparam RC_RESP_TIME = 4'd5;
+ localparam RC_RESP_DATA = 4'd6;
+
+ wire IS_EC = ctrl_tdata[63];
+ wire HAS_TIME = ctrl_tdata[61];
+ reg HAS_TIME_reg;
+
+ reg [3:0] rc_state;
+ reg [63:0] cmd_time;
+
+ wire now, late, go;
+ reg [11:0] seqnum;
+ reg [31:0] sid;
+
+ always @(posedge clk)
+ if(reset)
+ begin
+ rc_state <= RC_HEAD;
+ HAS_TIME_reg <= 1'b0;
+ sid <= 32'd0;
+ seqnum <= 12'd0;
+ end
+ else
+ case(rc_state)
+ RC_HEAD :
+ if(ctrl_tvalid)
+ begin
+ sid <= ctrl_tdata[31:0];
+ seqnum <= ctrl_tdata[59:48];
+ HAS_TIME_reg <= HAS_TIME;
+ if(IS_EC)
+ if(HAS_TIME)
+ rc_state <= RC_TIME;
+ else
+ rc_state <= RC_DATA;
+ else
+ if(~ctrl_tlast)
+ rc_state <= RC_DUMP;
+ end
+
+ RC_TIME :
+ if(ctrl_tvalid)
+ if(ctrl_tlast)
+ rc_state <= RC_RESP_HEAD;
+ else if(go)
+ rc_state <= RC_DATA;
+
+ RC_DATA :
+ if(ctrl_tvalid)
+ if(ready)
+ if(ctrl_tlast)
+ rc_state <= RC_RESP_HEAD;
+ else
+ rc_state <= RC_DUMP;
+
+ RC_DUMP :
+ if(ctrl_tvalid)
+ if(ctrl_tlast)
+ rc_state <= RC_RESP_HEAD;
+
+ RC_RESP_HEAD :
+ if(resp_tready)
+ rc_state <= RC_RESP_TIME;
+
+ RC_RESP_TIME :
+ if(resp_tready)
+ rc_state <= RC_RESP_DATA;
+
+ RC_RESP_DATA:
+ if(resp_tready)
+ rc_state <= RC_HEAD;
+
+ default :
+ rc_state <= RC_HEAD;
+ endcase // case (rc_state)
+
+ always @*
+ case (rc_state)
+ RC_HEAD : ctrl_tready <= 1'b1;
+ RC_TIME : ctrl_tready <= ctrl_tlast | go;
+ RC_DATA : ctrl_tready <= ready;
+ RC_DUMP : ctrl_tready <= 1'b1;
+ default : ctrl_tready <= 1'b0;
+ endcase // case (rc_state)
+
+ time_compare time_compare
+ (.clk(clk), .reset(reset), .time_now(vita_time), .trigger_time(ctrl_tdata), .now(now), .early(), .late(late), .too_early());
+
+ assign go = now | late;
+
+ assign set_stb = (rc_state == RC_DATA) & ready & ctrl_tvalid;
+ assign set_addr = ctrl_tdata[39:32];
+ assign set_data = ctrl_tdata[31:0];
+
+ always @*
+ case (rc_state)
+ RC_RESP_HEAD : { resp_tlast, resp_tdata } <= {1'b0, 4'hA, seqnum, 16'd24, sid[15:0], sid[31:16] };
+ RC_RESP_TIME : { resp_tlast, resp_tdata } <= {1'b0, vita_time};
+ RC_RESP_DATA : { resp_tlast, resp_tdata } <= {1'b1, readback};
+ default : { resp_tlast, resp_tdata } <= 65'h0;
+ endcase // case (rc_state)
+
+ assign resp_tvalid = (rc_state == RC_RESP_HEAD) | (rc_state == RC_RESP_TIME) | (rc_state == RC_RESP_DATA);
+
+endmodule // radio_ctrl_proc
+
diff --git a/fpga/usrp3/lib/control/radio_ctrl_proc_tb.v b/fpga/usrp3/lib/control/radio_ctrl_proc_tb.v
new file mode 100644
index 000000000..da863f4ea
--- /dev/null
+++ b/fpga/usrp3/lib/control/radio_ctrl_proc_tb.v
@@ -0,0 +1,104 @@
+`timescale 1ns/1ps
+
+module radio_ctrl_proc_tb();
+
+ reg clk = 0;
+ reg reset = 1;
+
+ always #10 clk = ~clk;
+
+ initial $dumpfile("radio_ctrl_proc_tb.vcd");
+ initial $dumpvars(0,radio_ctrl_proc_tb);
+
+ initial
+ begin
+ #1000 reset = 0;
+ #20000;
+ $finish;
+ end
+
+ reg [63:0] vita_time = 64'd0;
+ always @(posedge clk)
+ if(reset) vita_time <= 64'd0;
+ else vita_time <= vita_time + 64'd1;
+
+ reg [63:0] tdata;
+ wire [63:0] tdata_int;
+ reg tlast;
+ wire tlast_int;
+ reg tvalid = 1'b0;
+ wire tvalid_int;
+ wire tready, tready_int;
+
+ wire [7:0] set_addr;
+ wire [31:0] set_data;
+ wire set_stb;
+ wire ready = 1'b1;
+
+ task send_packet;
+ input ec;
+ input timed;
+ input [11:0] seqnum;
+ input [31:0] sid;
+ input [63:0] vtime;
+ input [15:0] addr;
+ input [31:0] data;
+
+ begin
+ // Send a packet
+ @(posedge clk);
+ tlast <= 1'b0;
+ tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid };
+ tvalid <= 1;
+ @(posedge clk);
+ if(timed)
+ begin
+ tdata <= vtime;
+ @(posedge clk);
+ end
+ tlast <= 1'b1;
+ tdata <= { 16'h0, addr, data };
+ @(posedge clk);
+ tvalid <= 0;
+ @(posedge clk);
+ end
+ endtask // send_packet
+
+ initial
+ begin
+ tvalid <= 1'b0;
+ while(reset)
+ @(posedge clk);
+ send_packet(1'b1,1'b0,12'h5,32'hDEAD_BEEF,64'h0,16'hB,32'hF00D_1234);
+ send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321);
+ send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
+ //send_packet(.ec(1), .timed(0), .seqnum(5), .sid(32'hDEAD_BEEF), .vtime(0), .addr(16'hB), .data(32'hF00D_1234));
+ end
+
+ axi_fifo_short #(.WIDTH(65)) axi_fifo_short
+ (.clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready),
+ .o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int));
+
+ wire [63:0] resp_tdata;
+ wire resp_tlast, resp_tvalid, resp_tready;
+
+ radio_ctrl_proc radio_ctrl_proc
+ (.clk(clk), .reset(reset), .clear(1'b0),
+ .ctrl_tdata(tdata_int), .ctrl_tlast(tlast_int), .ctrl_tvalid(tvalid_int), .ctrl_tready(tready_int),
+ .resp_tdata(resp_tdata), .resp_tlast(resp_tlast), .resp_tvalid(resp_tvalid), .resp_tready(resp_tready),
+ .vita_time(vita_time), .ready(ready),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .debug()
+ );
+
+ assign resp_tready = 1'b1;
+
+ always @(posedge clk)
+ if(resp_tvalid & resp_tready)
+ begin
+ $display("%x",resp_tdata);
+ if(resp_tlast)
+ $display("TLAST");
+ end
+endmodule // radio_ctrl_proc_tb
diff --git a/fpga/usrp3/lib/control/ram_2port.v b/fpga/usrp3/lib/control/ram_2port.v
new file mode 100644
index 000000000..434af0ff3
--- /dev/null
+++ b/fpga/usrp3/lib/control/ram_2port.v
@@ -0,0 +1,49 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+
+
+
+
+module ram_2port
+ #(parameter DWIDTH=32,
+ parameter AWIDTH=9)
+ (input clka,
+ input ena,
+ input wea,
+ input [AWIDTH-1:0] addra,
+ input [DWIDTH-1:0] dia,
+ output reg [DWIDTH-1:0] doa,
+
+ input clkb,
+ input enb,
+ input web,
+ input [AWIDTH-1:0] addrb,
+ input [DWIDTH-1:0] dib,
+ output reg [DWIDTH-1:0] dob);
+
+ reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
+ /*
+ integer i;
+ initial
+ for(i=0;i<(1<<AWIDTH);i=i+1)
+ ram[i] <= {DWIDTH{1'b0}};
+ */
+
+ always @(posedge clka) begin
+ if (ena)
+ begin
+ if (wea)
+ ram[addra] <= dia;
+ doa <= ram[addra];
+ end
+ end
+ always @(posedge clkb) begin
+ if (enb)
+ begin
+ if (web)
+ ram[addrb] <= dib;
+ dob <= ram[addrb];
+ end
+ end
+endmodule // ram_2port
diff --git a/fpga/usrp3/lib/control/reset_sync.v b/fpga/usrp3/lib/control/reset_sync.v
new file mode 100644
index 000000000..da284e62e
--- /dev/null
+++ b/fpga/usrp3/lib/control/reset_sync.v
@@ -0,0 +1,28 @@
+//
+// Copyright 2011 Ettus Research LLC
+//
+
+
+
+
+module reset_sync
+ (input clk,
+ input reset_in,
+ output reset_out);
+
+ reg reset_int;
+
+ reg reset_out_tmp;
+
+ //synthesis attribute async_reg of reset_out_tmp is "true";
+ //synthesis attribute async_reg of reset_int is "true";
+ always @(posedge clk or posedge reset_in)
+ if(reset_in)
+ {reset_out_tmp,reset_int} <= 2'b11;
+ else
+ {reset_out_tmp,reset_int} <= {reset_int,1'b0};
+
+ assign reset_out = reset_out_tmp;
+
+
+endmodule // reset_sync
diff --git a/fpga/usrp3/lib/control/setting_reg.v b/fpga/usrp3/lib/control/setting_reg.v
new file mode 100644
index 000000000..1664f54e2
--- /dev/null
+++ b/fpga/usrp3/lib/control/setting_reg.v
@@ -0,0 +1,35 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+
+
+//----------------------------------------------------------------------
+//-- A settings register is a peripheral for the settings register bus.
+//-- When the settings register sees strobe abd a matching address,
+//-- the outputs will be become registered to the given input bus.
+//----------------------------------------------------------------------
+
+module setting_reg
+ #(parameter my_addr = 0,
+ parameter awidth = 8,
+ parameter width = 32,
+ parameter at_reset=0)
+ (input clk, input rst, input strobe, input wire [awidth-1:0] addr,
+ input wire [31:0] in, output reg [width-1:0] out, output reg changed);
+
+ always @(posedge clk)
+ if(rst)
+ begin
+ out <= at_reset;
+ changed <= 1'b0;
+ end
+ else
+ if(strobe & (my_addr==addr))
+ begin
+ out <= in[width-1:0];
+ changed <= 1'b1;
+ end
+ else
+ changed <= 1'b0;
+
+endmodule // setting_reg
diff --git a/fpga/usrp3/lib/control/settings_bus_crossclock.v b/fpga/usrp3/lib/control/settings_bus_crossclock.v
new file mode 100644
index 000000000..2a6e7e7ef
--- /dev/null
+++ b/fpga/usrp3/lib/control/settings_bus_crossclock.v
@@ -0,0 +1,26 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+
+
+
+
+// This module takes the settings bus on one clock domain and crosses it over to another domain
+// Typically it will be used with the input settings bus on the wishbone clock, and either
+// the system or dsp clock on the output side
+
+module settings_bus_crossclock
+ #(parameter FLOW_CTRL=0, parameter AWIDTH=8, parameter DWIDTH=32)
+ (input clk_i, input rst_i, input set_stb_i, input [AWIDTH-1:0] set_addr_i, input [DWIDTH-1:0] set_data_i,
+ input clk_o, input rst_o, output set_stb_o, output [AWIDTH-1:0] set_addr_o, output [DWIDTH-1:0] set_data_o, input blocked);
+
+ wire nfull, nempty;
+
+ axi_fifo_2clk #(.WIDTH(AWIDTH + DWIDTH), .SIZE(0)) settings_fifo
+ (.reset(rst_i),
+ .i_aclk(clk_i), .i_tdata({set_addr_i,set_data_i}), .i_tvalid(set_stb_i), .i_tready(nfull),
+ .o_aclk(clk_o), .o_tdata({set_addr_o,set_data_o}), .o_tready(set_stb_o), .o_tvalid(nempty));
+
+ assign set_stb_o = nempty & (~blocked | ~FLOW_CTRL);
+
+endmodule // settings_bus_crossclock
diff --git a/fpga/usrp3/lib/control/simple_i2c_core.v b/fpga/usrp3/lib/control/simple_i2c_core.v
new file mode 100644
index 000000000..47f1ac82a
--- /dev/null
+++ b/fpga/usrp3/lib/control/simple_i2c_core.v
@@ -0,0 +1,104 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+
+
+// Simple I2C core
+
+// Settings reg map:
+//
+// BASE+0 control register
+// byte0 - control bits, data byte, or command bits, prescaler
+// byte1 - what to do? (documented in cpp file)
+// write prescaler lo
+// write prescaler hi
+// write control
+// write data
+// write command
+// read data
+// read status
+//
+
+// Readback:
+//
+// byte0 has readback value based on the last read command
+//
+
+module simple_i2c_core
+ #(
+ //settings register base address
+ parameter BASE = 0,
+
+ //i2c line level at reset
+ parameter ARST_LVL = 1
+ )
+ (
+ //clock and synchronous reset
+ input clock, input reset,
+
+ //32-bit settings bus inputs
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+
+ //32-bit data readback
+ output reg [31:0] readback,
+
+ //read is high when i2c core can begin another transaction
+ output reg ready,
+
+ // I2C signals
+ // i2c clock line
+ input scl_pad_i, // SCL-line input
+ output scl_pad_o, // SCL-line output (always 1'b0)
+ output scl_padoen_o, // SCL-line output enable (active low)
+
+ // i2c data line
+ input sda_pad_i, // SDA-line input
+ output sda_pad_o, // SDA-line output (always 1'b0)
+ output sda_padoen_o, // SDA-line output enable (active low)
+
+ //optional debug output
+ output [31:0] debug
+ );
+
+ //declare command settings register
+ wire [7:0] sr_what, sr_data;
+ wire sr_changed;
+ setting_reg #(.my_addr(BASE+0),.width(16)) i2c_cmd_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out({sr_what, sr_data}),.changed(sr_changed));
+
+ //declare wb interface signals
+ wire [2:0] wb_addr;
+ wire [7:0] wb_data_mosi;
+ wire [7:0] wb_data_miso;
+ wire wb_we, wb_stb, wb_cyc;
+ wire wb_ack;
+
+ //create wishbone-based i2c core
+ i2c_master_top #(.ARST_LVL(ARST_LVL)) i2c
+ (.wb_clk_i(clock),.wb_rst_i(reset),.arst_i(1'b0),
+ .wb_adr_i(wb_addr),.wb_dat_i(wb_data_mosi),.wb_dat_o(wb_data_miso),
+ .wb_we_i(wb_we),.wb_stb_i(wb_stb),.wb_cyc_i(wb_cyc),
+ .wb_ack_o(wb_ack),.wb_inta_o(),
+ .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_padoen_o),
+ .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_padoen_o) );
+
+ //not ready between setting register and wishbone ack
+ always @(posedge clock) begin
+ if (reset || wb_ack) ready <= 1;
+ else if (sr_changed) ready <= 0;
+ end
+
+ //register wishbone data on every ack
+ always @(posedge clock) begin
+ if (wb_ack) readback <= {24'b0, wb_data_miso};
+ end
+
+ //assign wishbone signals
+ assign wb_addr = sr_what[2:0];
+ assign wb_stb = sr_changed;
+ assign wb_we = wb_stb && sr_what[3];
+ assign wb_cyc = wb_stb;
+ assign wb_data_mosi = sr_data;
+
+endmodule //simple_i2c_core
diff --git a/fpga/usrp3/lib/control/simple_spi_core.v b/fpga/usrp3/lib/control/simple_spi_core.v
new file mode 100644
index 000000000..a18e00709
--- /dev/null
+++ b/fpga/usrp3/lib/control/simple_spi_core.v
@@ -0,0 +1,207 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+
+
+// Simple SPI core, the simplest, yet complete spi core I can think of
+
+// Settings register controlled.
+// 2 settings regs, control and data
+// 1 32-bit readback and status signal
+
+// Settings reg map:
+//
+// BASE+0 divider setting
+// bits [15:0] spi clock divider
+//
+// BASE+1 configuration input
+// bits [23:0] slave select, bit0 = slave0 enabled
+// bits [29:24] num bits (1 through 32)
+// bit [30] data input edge = in data bit latched on rising edge of clock
+// bit [31] data output edge = out data bit latched on rising edge of clock
+//
+// BASE+2 input data
+// Writing this register begins a spi transaction.
+// Bits are latched out from bit 0.
+// Therefore, load this register in reverse.
+//
+// Readback
+// Bits are latched into bit 0.
+// Therefore, data will be in-order.
+
+module simple_spi_core
+ #(
+ //settings register base address
+ parameter BASE = 0,
+
+ //width of serial enables (up to 24 is possible)
+ parameter WIDTH = 8,
+
+ //idle state of the spi clock
+ parameter CLK_IDLE = 0,
+
+ //idle state of the serial enables
+ parameter SEN_IDLE = 24'hffffff
+ )
+ (
+ //clock and synchronous reset
+ input clock, input reset,
+
+ //32-bit settings bus inputs
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+
+ //32-bit data readback
+ output [31:0] readback,
+
+ //read is high when spi core can begin another transaction
+ output ready,
+
+ //spi interface, slave selects, clock, data in, data out
+ output [WIDTH-1:0] sen,
+ output sclk,
+ output mosi,
+ input miso,
+
+ //optional debug output
+ output [31:0] debug
+ );
+
+ wire [15:0] sclk_divider;
+ setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out(sclk_divider),.changed());
+
+ wire [23:0] slave_select;
+ wire [5:0] num_bits;
+ wire datain_edge, dataout_edge;
+ setting_reg #(.my_addr(BASE+1),.width(32)) config_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
+
+ wire [31:0] mosi_data;
+ wire trigger_spi;
+ setting_reg #(.my_addr(BASE+2),.width(32)) data_sr(
+ .clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
+ .out(mosi_data),.changed(trigger_spi));
+
+ localparam WAIT_TRIG = 0;
+ localparam PRE_IDLE = 1;
+ localparam CLK_REG = 2;
+ localparam CLK_INV = 3;
+ localparam POST_IDLE = 4;
+ localparam IDLE_SEN = 5;
+
+ reg [2:0] state;
+
+ reg ready_reg;
+ assign ready = ready_reg && ~trigger_spi;
+
+ //serial clock either idles or is in one of two clock states
+ reg sclk_reg;
+ assign sclk = sclk_reg;
+
+ //serial enables either idle or enabled based on state
+ wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
+ wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
+ reg [WIDTH-1:0] sen_reg;
+ always @(posedge clock) sen_reg <= sen24[WIDTH-1:0];
+ assign sen = sen_reg;
+
+ //data output shift register
+ reg [31:0] dataout_reg;
+ wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
+ assign mosi = dataout_reg[31];
+
+ //data input shift register
+ // IJB. One pipeline stage to break critical path from register in I/O pads.
+ reg miso_pipe;
+ always @(posedge clock)
+ miso_pipe = miso;
+
+ reg [31:0] datain_reg;
+ wire [31:0] datain_next = {datain_reg[30:0], miso_pipe};
+ assign readback = datain_reg;
+
+ //counter for spi clock
+ reg [15:0] sclk_counter;
+ wire sclk_counter_done = (sclk_counter == sclk_divider);
+ wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
+
+ //counter for latching bits miso/mosi
+ reg [6:0] bit_counter;
+ wire [6:0] bit_counter_next = bit_counter + 1;
+ wire bit_counter_done = (bit_counter_next == num_bits);
+
+ always @(posedge clock) begin
+ if (reset) begin
+ state <= WAIT_TRIG;
+ sclk_reg <= CLK_IDLE;
+ ready_reg <= 0;
+ end
+ else begin
+ case (state)
+
+ WAIT_TRIG: begin
+ if (trigger_spi) state <= PRE_IDLE;
+ ready_reg <= ~trigger_spi;
+ dataout_reg <= mosi_data;
+ sclk_counter <= 0;
+ bit_counter <= 0;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ PRE_IDLE: begin
+ if (sclk_counter_done) state <= CLK_REG;
+ sclk_counter <= sclk_counter_next;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ CLK_REG: begin
+ if (sclk_counter_done) begin
+ state <= CLK_INV;
+ if (datain_edge != CLK_IDLE) datain_reg <= datain_next;
+ if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
+ sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
+ end
+ sclk_counter <= sclk_counter_next;
+ end
+
+ CLK_INV: begin
+ if (sclk_counter_done) begin
+ state <= (bit_counter_done)? POST_IDLE : CLK_REG;
+ bit_counter <= bit_counter_next;
+ if (datain_edge == CLK_IDLE) datain_reg <= datain_next;
+ if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
+ sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
+ end
+ sclk_counter <= sclk_counter_next;
+ end
+
+ POST_IDLE: begin
+ if (sclk_counter_done) state <= IDLE_SEN;
+ sclk_counter <= sclk_counter_next;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ IDLE_SEN: begin
+ if (sclk_counter_done) state <= WAIT_TRIG;
+ sclk_counter <= sclk_counter_next;
+ sclk_reg <= CLK_IDLE;
+ end
+
+ default: state <= WAIT_TRIG;
+
+ endcase //state
+ end
+ end
+
+ assign debug = {
+ trigger_spi, state, //4
+ sclk, mosi, miso, ready, //4
+ //sen[7:0], //8
+ 1'b0, bit_counter[6:0], //8
+ sclk_counter_done, bit_counter_done, //2
+ sclk_counter[5:0] //6
+ };
+
+endmodule //simple_spi_core