diff options
| author | Ben Hilburn <ben.hilburn@ettus.com> | 2013-10-10 10:17:27 -0700 | 
|---|---|---|
| committer | Ben Hilburn <ben.hilburn@ettus.com> | 2013-10-10 10:17:27 -0700 | 
| commit | 0df4b801a34697f2058b4a7b95e08d2a0576c9db (patch) | |
| tree | be10e78d1a97c037a9e7492360a178d1873b9c09 /fpga/usrp3/lib/control | |
| parent | 6e7bc850b66e8188718248b76b729c7cf9c89700 (diff) | |
| download | uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.tar.gz uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.tar.bz2 uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.zip | |
Squashed B200 FPGA Source. Code from Josh Blum, Ian Buckley, and Matt Ettus.
Diffstat (limited to 'fpga/usrp3/lib/control')
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 | 
