diff options
31 files changed, 913 insertions, 543 deletions
| diff --git a/fpga/usrp2/control_lib/Makefile.srcs b/fpga/usrp2/control_lib/Makefile.srcs index 751b40828..5ae185ee8 100644 --- a/fpga/usrp2/control_lib/Makefile.srcs +++ b/fpga/usrp2/control_lib/Makefile.srcs @@ -50,4 +50,9 @@ bootram.v \  nsgpio16LE.v \  settings_bus_16LE.v \  atr_controller16.v \ +newfifo/fifo_pacer.v \ +newfifo/packet_generator32.v \ +newfifo/packet_generator.v \ +newfifo/packet_verifier32.v \ +newfifo/packet_verifier.v \  )) diff --git a/fpga/usrp2/top/u1e/u1e_core.v b/fpga/usrp2/top/u1e/u1e_core.v index d590b4fb1..7d5924bea 100644 --- a/fpga/usrp2/top/u1e/u1e_core.v +++ b/fpga/usrp2/top/u1e/u1e_core.v @@ -29,18 +29,22 @@ module u1e_core     localparam TXFIFOSIZE = 13;     localparam RXFIFOSIZE = 13; -   localparam SR_RX_DSP = 0;     // 5 regs -   localparam SR_CLEAR_FIFO = 6; // 1 reg -   localparam SR_RX_CTRL = 8;    // 9 regs -   localparam SR_TX_DSP = 17;    // 5 regs -   localparam SR_TX_CTRL = 24;   // 2 regs -   localparam SR_TIME64 = 28;    // 4 regs +   // 64 total regs in address space +   localparam SR_RX_CTRL = 0;     // 9 regs (+0 to +8) +   localparam SR_RX_DSP = 16;     // 7 regs (+0 to +6) +   localparam SR_TX_CTRL = 24;    // 6 regs (+0 to +5) +   localparam SR_TX_DSP = 32;     // 5 regs (+0 to +4) +   localparam SR_TIME64 = 40;     // 6 regs (+0 to +5) +   localparam SR_CLEAR_RX_FIFO = 48; // 1 reg +   localparam SR_CLEAR_TX_FIFO = 49; // 1 reg +   localparam SR_GLOBAL_RESET = 50; // 1 reg +   localparam SR_REG_TEST32 = 52; // 1 reg     wire [7:0]	COMPAT_NUM = 8'd3;     wire 	wb_clk = clk_fpga; -   wire 	wb_rst = rst_fpga; -    +   wire 	wb_rst, global_reset; +     wire 	pps_int;     wire [63:0] 	vita_time, vita_time_pps;     reg [15:0] 	reg_leds, reg_cgen_ctrl, reg_test, xfer_rate; @@ -51,6 +55,12 @@ module u1e_core     wire [31:0] 	debug_vt; +   setting_reg #(.my_addr(SR_GLOBAL_RESET), .width(1)) sr_reset +     (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(),.changed(global_reset)); + +   reset_sync reset_sync(.clk(wb_clk), .reset_in(rst_fpga | global_reset), .reset_out(wb_rst)); +        // /////////////////////////////////////////////////////////////////////////////////////     // GPMC Slave to Wishbone Master     localparam dw = 16; @@ -72,15 +82,16 @@ module u1e_core     wire [7:0] 	 rate;     wire 	 bus_error; - -   wire 	 clear_rx_int, clear_tx_int, clear_tx, clear_rx, do_clear; +   wire 	 clear_tx, clear_rx; -   setting_reg #(.my_addr(SR_CLEAR_FIFO), .width(2)) sr_clear +   setting_reg #(.my_addr(SR_CLEAR_RX_FIFO), .width(1)) sr_clear_rx       (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), -      .in(set_data),.out({clear_tx_int,clear_rx_int}),.changed(do_clear)); -   assign clear_tx = clear_tx_int & do_clear; -   assign clear_rx = clear_rx_int & do_clear; -    +      .in(set_data),.out(),.changed(clear_rx)); + +   setting_reg #(.my_addr(SR_CLEAR_TX_FIFO), .width(1)) sr_clear_tx +     (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(),.changed(clear_tx)); +     gpmc_async #(.TXFIFOSIZE(TXFIFOSIZE), .RXFIFOSIZE(RXFIFOSIZE))     gpmc (.arst(wb_rst),  	 .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), @@ -149,7 +160,8 @@ module u1e_core        .src1_rdy_i(rx_src_rdy_int), .dst1_rdy_o(rx_dst_rdy_int),        .src2_rdy_o(rx_src_rdy), .dst2_rdy_i(rx_dst_rdy),        .underrun(), .overrun(rx_overrun)); -    + +   wire 	 run_tx, run_rx, strobe_tx, strobe_rx;  `endif //  `ifdef TIMED  `ifdef DSP @@ -203,7 +215,9 @@ module u1e_core     wire 	 run_tx;     vita_tx_chain #(.BASE_CTRL(SR_TX_CTRL), .BASE_DSP(SR_TX_DSP),  -		   .REPORT_ERROR(1), .PROT_ENG_FLAGS(0))  +		   .REPORT_ERROR(1), .DO_FLOW_CONTROL(0), +		   .PROT_ENG_FLAGS(0), .USE_TRANS_HEADER(0), +		   .DSP_NUMBER(0))      vita_tx_chain       (.clk(wb_clk), .reset(wb_rst),        .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), @@ -258,7 +272,7 @@ module u1e_core  		.s2_addr(4'h2), .s2_mask(4'hF),	.s3_addr(4'h3), .s3_mask(4'hF),  		.s4_addr(4'h4), .s4_mask(4'hF),	.s5_addr(4'h5), .s5_mask(4'hF),  		.s6_addr(4'h6), .s6_mask(4'hF),	.s7_addr(4'h7), .s7_mask(4'hF), -		.s8_addr(4'h8), .s8_mask(4'hF),	.s9_addr(4'h9), .s9_mask(4'hF), +		.s8_addr(4'h8), .s8_mask(4'hE),	.s9_addr(4'hf), .s9_mask(4'hF), // slave 8 is double wide  		.sa_addr(4'ha), .sa_mask(4'hF),	.sb_addr(4'hb), .sb_mask(4'hF),  		.sc_addr(4'hc), .sc_mask(4'hF),	.sd_addr(4'hd), .sd_mask(4'hF),  		.se_addr(4'he), .se_mask(4'hF),	.sf_addr(4'hf), .sf_mask(4'hF)) @@ -299,7 +313,7 @@ module u1e_core        .sf_dat_o(sf_dat_mosi),.sf_adr_o(sf_adr),.sf_sel_o(sf_sel),.sf_we_o(sf_we),.sf_cyc_o(sf_cyc),.sf_stb_o(sf_stb),        .sf_dat_i(sf_dat_miso),.sf_ack_i(sf_ack),.sf_err_i(0),.sf_rty_i(0) ); -   assign s8_ack = 0;   assign s9_ack = 0;   assign sa_ack = 0;   assign sb_ack = 0; +   assign s5_ack = 0;   assign s9_ack = 0;   assign sa_ack = 0;   assign sb_ack = 0;     assign sc_ack = 0;   assign sd_ack = 0;   assign se_ack = 0;   assign sf_ack = 0;     // ///////////////////////////////////////////////////////////////////////////////////// @@ -409,12 +423,12 @@ module u1e_core  		.gpio( {io_tx,io_rx} ) );     // ///////////////////////////////////////////////////////////////////////// -   // Settings Bus -- Slave #5 +   // Settings Bus -- Slave #8 + 9 -   // only have 32 regs, 32 bits each with current setup... -   settings_bus_16LE #(.AWIDTH(11),.RWIDTH(11-4-2)) settings_bus_16LE -     (.wb_clk(wb_clk),.wb_rst(wb_rst),.wb_adr_i(s5_adr),.wb_dat_i(s5_dat_mosi), -      .wb_stb_i(s5_stb),.wb_we_i(s5_we),.wb_ack_o(s5_ack), +   // only have 64 regs, 32 bits each with current setup... +   settings_bus_16LE #(.AWIDTH(11),.RWIDTH(6)) settings_bus_16LE +     (.wb_clk(wb_clk),.wb_rst(wb_rst),.wb_adr_i(s8_adr),.wb_dat_i(s8_dat_mosi), +      .wb_stb_i(s8_stb),.wb_we_i(s8_we),.wb_ack_o(s8_ack),        .strobe(set_stb),.addr(set_addr),.data(set_data) );     // ///////////////////////////////////////////////////////////////////////// @@ -429,15 +443,24 @@ module u1e_core     // /////////////////////////////////////////////////////////////////////////     // Readback mux 32 -- Slave #7 +   wire [31:0] reg_test32; + +   setting_reg #(.my_addr(SR_REG_TEST32)) sr_reg_test32 +     (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), +      .in(set_data),.out(reg_test32),.changed()); +     wb_readback_mux_16LE readback_mux_32       (.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s7_stb),        .wb_adr_i(s7_adr), .wb_dat_o(s7_dat_miso), .wb_ack_o(s7_ack), -      .word00(vita_time[63:32]),    .word01(vita_time[31:0]), -      .word02(vita_time_pps[63:32]),.word03(vita_time_pps[31:0]), -      .word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0), -      .word08(32'b0),.word09(32'b0),.word10(32'b0),.word11(32'b0), -      .word12(32'b0),.word13(32'b0),.word14(32'b0),.word15(32'b0) +      .word00(vita_time[63:32]),        .word01(vita_time[31:0]), +      .word02(vita_time_pps[63:32]),    .word03(vita_time_pps[31:0]), +      .word04(reg_test32),              .word05(32'b0), +      .word06(32'b0),                   .word07(32'b0), +      .word08(32'b0),                   .word09(32'b0), +      .word10(32'b0),                   .word11(32'b0), +      .word12(32'b0),                   .word13(32'b0), +      .word14(32'b0),                   .word15(32'b0)        );     // ///////////////////////////////////////////////////////////////////////// diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index e64c1031f..6b2ac4e64 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -183,6 +183,8 @@ IF(ENABLE_UTILS)      ADD_SUBDIRECTORY(utils)  ENDIF(ENABLE_UTILS) +ADD_SUBDIRECTORY(usrp_e_utils) +  ########################################################################  # Print Summary  ######################################################################## diff --git a/host/apps/omap_debug/usrp-e-crc-rw.c b/host/apps/omap_debug/usrp-e-crc-rw.c index c3ae45cc1..c6f3427f0 100644 --- a/host/apps/omap_debug/usrp-e-crc-rw.c +++ b/host/apps/omap_debug/usrp-e-crc-rw.c @@ -8,11 +8,19 @@  #include <stdlib.h>  #include <unistd.h>  #include <stddef.h> +#include <poll.h> +#include <sys/mman.h>  #include "usrp_e.h"  // max length #define PKT_DATA_LENGTH 1016  static int packet_data_length; +struct ring_buffer_info (*rxi)[]; +struct ring_buffer_info (*txi)[]; +__u8 *rx_buf; +__u8 *tx_buf; +static struct usrp_e_ring_buffer_size_t rb_size; +  static int fp;  static u_int32_t crc_tab[256]; @@ -44,8 +52,7 @@ static u_int32_t chksum_crc32_gentab(void)  static void *read_thread(void *threadid)  {  	int cnt; -	struct usrp_transfer_frame *rx_data; -	int rx_pkt_cnt; +	int rx_pkt_cnt, rb_read;  	int i;  	unsigned long crc;  	unsigned int rx_crc; @@ -53,70 +60,56 @@ static void *read_thread(void *threadid)  	struct timeval start_time, finish_time;  	__u8 *p; -	__u32 *pi;  	printf("Greetings from the reading thread!\n");  	// IMPORTANT: must assume max length packet from fpga -	rx_data = malloc(2048); - +	  	rx_pkt_cnt = 0; +	rb_read = 0;  	bytes_transfered = 0;  	gettimeofday(&start_time, NULL);  	while (1) { -		cnt = read(fp, rx_data, 2048); -		if (cnt < 0) -			printf("Error returned from read: %d\n", cnt); - -		rx_pkt_cnt++; - -#if 0 -		if (rx_pkt_cnt  == 512) { -			printf("."); -			fflush(stdout); -			rx_pkt_cnt = 0; +		while (!((*rxi)[rb_read].flags & RB_USER)) { +			struct pollfd pfd; +			pfd.fd = fp; +			pfd.events = POLLIN; +			ssize_t ret = poll(&pfd, 1, -1);  		} -#endif +		(*rxi)[rb_read].flags = RB_USER_PROCESS; -		if (rx_data->status & RB_OVERRUN) -			printf("O"); - -		printf("rx_data->len = %d\n", rx_data->len); +		rx_pkt_cnt++; +		cnt = (*rxi)[rb_read].len; +		p = rx_buf + (rb_read * 2048); -	 +		rx_crc = *(int *) &p[cnt-4];  		crc = 0xFFFFFFFF; -		for (i = 0; i < rx_data->len - 4; i+=2) { +		for (i = 0; i < cnt - 4; i+=2) {  			crc = ((crc >> 8) & 0x00FFFFFF) ^ -				crc_tab[(crc ^ rx_data->buf[i+1]) & 0xFF]; -printf("idx = %d, data = %X, crc = %X\n", i, rx_data->buf[i+1],crc); +				crc_tab[(crc ^ p[i+1]) & 0xFF]; +//printf("idx = %d, data = %X, crc = %X\n", i, p[i+1],crc);  			crc = ((crc >> 8) & 0x00FFFFFF) ^ -				crc_tab[(crc ^ rx_data->buf[i]) & 0xFF]; -printf("idx = %d, data = %X, crc = %X\n", i, rx_data->buf[i],crc); +				crc_tab[(crc ^ p[i]) & 0xFF]; +//printf("idx = %d, data = %X, crc = %X\n", i, p[i],crc);  		} -		p = &rx_data->buf[rx_data->len - 4]; -		pi = (__u32 *) p; -		rx_crc = *pi; - -#if 1 -		printf("rx_data->len = %d\n", rx_data->len); -		printf("rx_data->status = %d\n", rx_data->status); -		for (i = 0; i < rx_data->len; i++) -			printf("idx = %d, data = %X\n", i, rx_data->buf[i]); -		printf("calc crc = %lX, rx crc = %X\n", crc, rx_crc);  -		fflush(stdout); -		break; -#endif +		(*rxi)[rb_read].flags = RB_KERNEL; +#if 0  		if (rx_crc != (crc & 0xFFFFFFFF)) {  			printf("CRC Error, calc crc: %X, rx_crc: %X\n",  				(crc & 0xFFFFFFFF), rx_crc);  		} +#endif + +		rb_read++; +		if (rb_read == rb_size.num_rx_frames) +			rb_read = 0; -		bytes_transfered += rx_data->len; +		bytes_transfered += cnt;  		if (bytes_transfered > (100 * 1000000)) {  			gettimeofday(&finish_time, NULL); @@ -124,7 +117,7 @@ printf("idx = %d, data = %X, crc = %X\n", i, rx_data->buf[i],crc);  			printf("Bytes transfered = %ld, elapsed seconds = %ld\n", bytes_transfered, elapsed_seconds);  			printf("RX data transfer rate = %f K Samples/second\n", -				(float) bytes_transfered / (float) elapsed_seconds / 250); +				(float) bytes_transfered / (float) elapsed_seconds / 4000);  			start_time = finish_time; @@ -135,17 +128,17 @@ printf("idx = %d, data = %X, crc = %X\n", i, rx_data->buf[i],crc);  static void *write_thread(void *threadid)  { -	int seq_number, i, cnt, tx_pkt_cnt; +	int i, tx_pkt_cnt, rb_write;  	int tx_len;  	unsigned long crc; -	struct usrp_transfer_frame *tx_data;  	unsigned long bytes_transfered, elapsed_seconds;  	struct timeval start_time, finish_time; +	__u8 *p;  	printf("Greetings from the write thread!\n"); +	rb_write = 0;  	tx_pkt_cnt = 0; -	tx_data = malloc(2048);  	bytes_transfered = 0;  	gettimeofday(&start_time, NULL); @@ -153,6 +146,14 @@ static void *write_thread(void *threadid)  	while (1) {  		tx_pkt_cnt++; +		p = tx_buf + (rb_write * 2048); + +//		printf("p = %p\n", p); + +		if (packet_data_length > 0) +			tx_len = packet_data_length; +		else +			tx_len = (random() & 0x1ff) + (2044 - 512);  #if 0  		if (tx_pkt_cnt  == 512) { @@ -170,25 +171,33 @@ static void *write_thread(void *threadid)  		}  #endif -		tx_len = 2048 - sizeof(struct usrp_transfer_frame) - sizeof(int); -		tx_data->len = tx_len + sizeof(int); +//		printf("Checking for space at rb entry = %d\n", rb_write); +		while (!((*txi)[rb_write].flags & RB_KERNEL)) { +			struct pollfd pfd; +			pfd.fd = fp; +			pfd.events = POLLOUT; +			ssize_t ret = poll(&pfd, 1, -1); +		} +//		printf("Got space\n");  		crc = 0xFFFFFFFF; -		for (i = 0; i < tx_len; i++) { -			tx_data->buf[i] = i & 0xFF; +		for (i = 0; i < tx_len-4; i++) { +			p[i] = i & 0xFF;  			crc = ((crc >> 8) & 0x00FFFFFF) ^ -				crc_tab[(crc ^ tx_data->buf[i]) & 0xFF]; +				crc_tab[(crc ^ p[i]) & 0xFF];  		} -		*((int *) &tx_data[tx_len]) = crc; +		*(int *) &p[tx_len-4] = crc; -		cnt = write(fp, tx_data, 2048); -		if (cnt < 0) -			printf("Error returned from write: %d\n", cnt); +		(*txi)[rb_write].len = tx_len; +		(*txi)[rb_write].flags = RB_USER; +		rb_write++; +		if (rb_write == rb_size.num_tx_frames) +			rb_write = 0; -		bytes_transfered += tx_data->len; +		bytes_transfered += tx_len;  		if (bytes_transfered > (100 * 1000000)) {  			gettimeofday(&finish_time, NULL); @@ -196,7 +205,7 @@ static void *write_thread(void *threadid)  			printf("Bytes transfered = %d, elapsed seconds = %d\n", bytes_transfered, elapsed_seconds);  			printf("TX data transfer rate = %f K Samples/second\n", -				(float) bytes_transfered / (float) elapsed_seconds / 250); +				(float) bytes_transfered / (float) elapsed_seconds / 4000);  			start_time = finish_time; @@ -213,13 +222,16 @@ int main(int argc, char *argv[])  	pthread_t tx, rx;  	long int t;  	int fpga_config_flag ,decimation; +	int ret, map_size, page_size; +	void *rb; +  	struct usrp_e_ctl16 d;  	struct sched_param s = {  		.sched_priority = 1  	};  	if (argc < 4) { -		printf("%s t|w|rw decimation data_size\n", argv[0]); +		printf("%s r|w|rw decimation data_size\n", argv[0]);  		return -1;  	} @@ -231,6 +243,29 @@ int main(int argc, char *argv[])  	fp = open("/dev/usrp_e0", O_RDWR);  	printf("fp = %d\n", fp); +	page_size = getpagesize(); + +	ret = ioctl(fp, USRP_E_GET_RB_INFO, &rb_size); + +	map_size = (rb_size.num_pages_rx_flags + rb_size.num_pages_tx_flags) * page_size + +		(rb_size.num_rx_frames + rb_size.num_tx_frames) * (page_size >> 1); + +	rb = mmap(0, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0); +	if (rb == MAP_FAILED) { +		perror("mmap failed"); +		return -1; +	} + +	printf("rb = %X\n", rb); + +	rxi = rb; +	rx_buf = rb + (rb_size.num_pages_rx_flags * page_size); +	txi = rb + (rb_size.num_pages_rx_flags * page_size) + +		(rb_size.num_rx_frames * page_size >> 1); +	tx_buf = rb + (rb_size.num_pages_rx_flags * page_size) + +		(rb_size.num_rx_frames * page_size >> 1) + +		(rb_size.num_pages_tx_flags * page_size); +  	fpga_config_flag = 0;  	if (strcmp(argv[1], "w") == 0)  		fpga_config_flag |= (1 << 15); diff --git a/host/apps/omap_debug/usrp-e-mm-loopback.c b/host/apps/omap_debug/usrp-e-mm-loopback.c index f5fc83c87..b67eecd21 100644 --- a/host/apps/omap_debug/usrp-e-mm-loopback.c +++ b/host/apps/omap_debug/usrp-e-mm-loopback.c @@ -75,6 +75,8 @@ static void *read_thread(void *threadid)  			ssize_t ret = poll(&pfd, 1, -1);  		} +		(*rxi)[rb_read].flags = RB_USER_PROCESS; +  //		printf("pkt received, rb_read = %d\n", rb_read);  		cnt = (*rxi)[rb_read].len; diff --git a/host/apps/omap_debug/usrp_e.h b/host/apps/omap_debug/usrp_e.h index f96706c4a..4c6a5dd89 100644 --- a/host/apps/omap_debug/usrp_e.h +++ b/host/apps/omap_debug/usrp_e.h @@ -34,16 +34,13 @@ struct usrp_e_ctl32 {  #define UE_SPI_TXRX	1  /* Defines for spi ctrl register */ -#define UE_SPI_CTRL_TXNEG	(BIT(10)) -#define UE_SPI_CTRL_RXNEG	(BIT(9)) +#define UE_SPI_CTRL_TXNEG	(1<<10) +#define UE_SPI_CTRL_RXNEG	(1<<9)  #define UE_SPI_PUSH_RISE	0  #define UE_SPI_PUSH_FALL	UE_SPI_CTRL_TXNEG  #define UE_SPI_LATCH_RISE	0  #define UE_SPI_LATCH_FALL	UE_SPI_CTRL_RXNEG -#define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28) - -#define USRP_E_COMPAT_NUMBER 1  struct usrp_e_spi {  	__u8 readback; @@ -68,12 +65,16 @@ struct usrp_e_i2c {  #define USRP_E_I2C_READ		_IOWR(USRP_E_IOC_MAGIC, 0x25, struct usrp_e_i2c)  #define USRP_E_I2C_WRITE	_IOW(USRP_E_IOC_MAGIC, 0x26, struct usrp_e_i2c)  #define USRP_E_GET_RB_INFO      _IOR(USRP_E_IOC_MAGIC, 0x27, struct usrp_e_ring_buffer_size_t) +#define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28) + +#define USRP_E_COMPAT_NUMBER 1  /* Flag defines */  #define RB_USER (1<<0)  #define RB_KERNEL (1<<1)  #define RB_OVERRUN (1<<2)  #define RB_DMA_ACTIVE (1<<3) +#define RB_USER_PROCESS (1<<4)  struct ring_buffer_info {  	int flags; diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index 0c2cadfc2..c04262b63 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -29,6 +29,7 @@ SET(manual_sources      transport.rst      usrp1.rst      usrp2.rst +    usrp_e1xx.rst  )  ######################################################################## diff --git a/host/docs/index.rst b/host/docs/index.rst index 6dac2680c..734300164 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -24,7 +24,9 @@ Application Notes  * `Device Identification Notes <./identification.html>`_  * `Firmware and FPGA Image Notes <./images.html>`_  * `USRP1 Application Notes <./usrp1.html>`_ -* `USRP2 and N Series Application Notes <./usrp2.html>`_ +* `USRP2 Application Notes <./usrp2.html>`_ +* `USRP-N2XX Series Application Notes <./usrp2.html>`_ +* `USRP-E1XX Series Application Notes <./usrp_e1xx.html>`_  * `Daughterboard Application Notes <./dboards.html>`_  * `Transport Application Notes <./transport.html>`_ diff --git a/host/docs/usrp_e1xx.rst b/host/docs/usrp_e1xx.rst new file mode 100644 index 000000000..ffcd370dd --- /dev/null +++ b/host/docs/usrp_e1xx.rst @@ -0,0 +1,65 @@ +======================================================================== +UHD - USRP-E1XX Series Application Notes +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Specify a non-standard image +------------------------------------------------------------------------ +The UHD will automatically select the USRP embedded FPGA image from the installed images package. +The FPGA image selection can be overridden with the "fpga" device address parameter. + +Example device address string representations to specify non-standard FPGA image: + +:: + +    fpga=usrp_e100_custom.bin + +------------------------------------------------------------------------ +Changing the master clock rate +------------------------------------------------------------------------ +The master clock rate of the USRP embedded feeds both the FPGA DSP and the codec chip. +UHD can dynamically reconfigure the clock rate though the set_master_clock_rate() API call. +Hundreds of rates between 32MHz and 64MHz are available. +A few notable rates are: + +* 64MHz - maximum rate of the codec chip +* 61.44MHz - good for UMTS/WCDMA applications +* 52Mhz - good for GSM applications + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set 61.44MHz - uses external VCXO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use the 61.44MHz clock rate, the USRP embedded will require two jumpers to be moved. + +* J16 is a two pin header, remove the jumper (or leave it on pin1 only) +* J15 is a three pin header, move the jumper to (pin1, pin2) + +For the correct clock settings, call usrp->set_master_clock_rate(61.44e6) +before any other parameters are set in your application. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set other rates - uses internal VCO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use other clock rates, the jumpers will need to be in the default position. + +* J16 is a two pin header, move the jumper to (pin1, pin2) +* J15 is a three pin header, move the jumper to (pin2, pin3) + +For the correct clock settings, call usrp->set_master_clock_rate(rate) +before any other parameters are set in your application. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Clock rate recovery - unbricking +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is possible to set a clock rate such that the UHD can no longer communicate with the FPGA. +When this occurs, it is necessary to use the usrp-e-utility to recover the clock generator. +The recovery utility works by loading a special pass-through FPGA image so the computer +can talk directly to the clock generator over a SPI interface. + +Run the following commands to restore the clock generator to a usable state: +:: + +    cd <prefix>/share/uhd/usrp_e_utilities +    ./usrp-e-utility --fpga=../images/usrp_e100_pt_fpga.bin --reclk diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 9ebe36c5a..630b4a7a9 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -32,7 +32,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::string args;      double seconds_in_future;      size_t total_num_samps; -    double rate, freq; +    double rate, freq, clock;      //setup the program options      po::options_description desc("Allowed options"); @@ -41,6 +41,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){          ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")          ("secs", po::value<double>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") +        ("clock", po::value<double>(&clock), "master clock frequency in Hz")          ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples")          ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz")          ("dilv", "specify to disable inner-loop verbose") @@ -63,6 +64,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);      std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; +    //optionally set the clock rate (do before setting anything else) +    if (vm.count("clock")){ +        std::cout << boost::format("Setting master clock rate: %f MHz...") % (clock/1e6) << std::endl; +        usrp->set_master_clock_rate(clock); +    } +      //set the rx sample rate      std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl;      usrp->set_rx_rate(rate); diff --git a/host/lib/ic_reg_maps/gen_ad9522_regs.py b/host/lib/ic_reg_maps/gen_ad9522_regs.py index a5debe568..86605c34a 100755 --- a/host/lib/ic_reg_maps/gen_ad9522_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9522_regs.py @@ -80,6 +80,14 @@ external_zero_delay_fcds    0x01E[4:3]               0  enable_external_zero_delay  0x01E[2]                 0  enable_zero_delay           0x01E[1]                 0  ######################################################################## +vco_calibration_finished    0x01F[6]                 0 +holdover_active             0x01F[5]                 0 +ref2_selected               0x01F[4]                 0 +vco_freq_gt_thresh          0x01F[3]                 0 +ref2_freq_gt_thresh         0x01F[2]                 0 +ref1_freq_gt_thresh         0x01F[1]                 0 +digital_lock_detect         0x01F[0]                 0 +########################################################################  #for $i in range(12)  #set $addr = ($i + 0x0F0)  out$(i)_format              $(addr)[7]             0             lvds, cmos diff --git a/host/lib/usrp/usrp_e100/CMakeLists.txt b/host/lib/usrp/usrp_e100/CMakeLists.txt index c32dd87f8..acbac177e 100644 --- a/host/lib/usrp/usrp_e100/CMakeLists.txt +++ b/host/lib/usrp/usrp_e100/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2010 Ettus Research LLC +# Copyright 2010-2011 Ettus Research LLC  #  # This program is free software: you can redistribute it and/or modify  # it under the terms of the GNU General Public License as published by @@ -40,7 +40,7 @@ IF(ENABLE_USRP_E100)          ${CMAKE_CURRENT_SOURCE_DIR}/dboard_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/dsp_impl.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/fpga-downloader.cc +        ${CMAKE_CURRENT_SOURCE_DIR}/fpga_downloader.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/mboard_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/usrp_e100_impl.cpp diff --git a/host/lib/usrp/usrp_e100/clock_ctrl.cpp b/host/lib/usrp/usrp_e100/clock_ctrl.cpp index 1fb1a7125..e29fe18ce 100644 --- a/host/lib/usrp/usrp_e100/clock_ctrl.cpp +++ b/host/lib/usrp/usrp_e100/clock_ctrl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -22,11 +22,26 @@  #include "usrp_e100_regs.hpp" //spi slave constants  #include <boost/assign/list_of.hpp>  #include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/math/common_factor_rt.hpp> //gcd +#include <algorithm>  #include <utility>  #include <iostream>  using namespace uhd; +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool CLOCK_SETTINGS_DEBUG = false; +static const bool ENABLE_THE_TEST_OUT = true; +static const double REFERENCE_INPUT_RATE = 10e6; +static const double DEFAULT_OUTPUT_RATE = 64e6; + +/*********************************************************************** + * Helpers + **********************************************************************/  template <typename div_type, typename bypass_type> static void set_clock_divider(      size_t divider, div_type &low, div_type &high, bypass_type &bypass  ){ @@ -36,24 +51,117 @@ template <typename div_type, typename bypass_type> static void set_clock_divider  }  /*********************************************************************** - * Constants + * Clock rate calculation stuff: + *   Using the internal VCO between 1400 and 1800 MHz   **********************************************************************/ -static const bool enable_test_clock = false; -static const size_t ref_clock_doubler = 2; //enabled below -static const double ref_clock_rate = 10e6 * ref_clock_doubler; +struct clock_settings_type{ +    size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider; +    size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;} +    double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;} +    double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();} +    double get_chan_rate(void) const{return get_vco_rate()/vco_divider;} +    double get_out_rate(void) const{return get_chan_rate()/chan_divider;} +    std::string to_pp_string(void) const{ +        return str(boost::format( +            "  r_counter: %d\n" +            "  a_counter: %d\n" +            "  b_counter: %d\n" +            "  prescaler: %d\n" +            "  vco_divider: %d\n" +            "  chan_divider: %d\n" +            "  vco_rate: %fMHz\n" +            "  chan_rate: %fMHz\n" +            "  out_rate: %fMHz\n" +            ) +            % r_counter +            % a_counter +            % b_counter +            % prescaler +            % vco_divider +            % chan_divider +            % (get_vco_rate()/1e6) +            % (get_chan_rate()/1e6) +            % (get_out_rate()/1e6) +        ); +    } +}; + +//! gives the greatest divisor of num between 1 and max inclusive +template<typename T> static inline T greatest_divisor(T num, T max){ +    for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1; +} + +//! gives the least divisor of num between min and num exclusive +template<typename T> static inline T least_divisor(T num, T min){ +    for (T i = min; i < num; i++) if (num%i == 0) return i; return 1; +} + +static clock_settings_type get_clock_settings(double rate){ +    clock_settings_type cs; +    cs.ref_clock_doubler = 2; //always doubling +    cs.prescaler = 8; //set to 8 when input is under 2400 MHz -static const size_t r_counter = 1; -static const size_t a_counter = 0; -static const size_t b_counter = 20 / ref_clock_doubler; -static const size_t prescaler = 8; //set below with enum, set to 8 when input is under 2400 MHz -static const size_t vco_divider = 5; //set below with enum +    //basic formulas used below: +    //out_rate*X = ref_rate*Y +    //X = i*ref_rate/gcd +    //Y = i*out_rate/gcd +    //X = chan_div * vco_div * R +    //Y = P*B + A -static const size_t n_counter = prescaler * b_counter + a_counter; -static const size_t vco_clock_rate = ref_clock_rate/r_counter * n_counter; //between 1400 and 1800 MHz -static const double master_clock_rate = vco_clock_rate/vco_divider; +    const boost::uint64_t out_rate = boost::uint64_t(rate); +    const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate()); +    const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate)); -static const size_t fpga_clock_divider = size_t(master_clock_rate/64e6); -static const size_t codec_clock_divider = size_t(master_clock_rate/64e6); +    for (size_t i = 1; i <= 100; i++){ +        const size_t X = i*ref_rate/gcd; +        const size_t Y = i*out_rate/gcd; + +        //determine A and B (P is fixed) +        cs.b_counter = Y/cs.prescaler; +        cs.a_counter = Y - cs.b_counter*cs.prescaler; + +        static const double vco_bound_pad = 100e6; +        for ( //calculate an r divider that fits into the bounds of the vco +            cs.r_counter  = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad)); +            cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad)) +            and cs.r_counter > 0; cs.r_counter++ +        ){ + +            //determine chan_div and vco_div +            //and fill in that order of preference +            cs.chan_divider = greatest_divisor<size_t>(X/cs.r_counter, 32); +            cs.vco_divider = greatest_divisor<size_t>(X/cs.chan_divider/cs.r_counter, 6); + +            //avoid a vco divider of 1 (if possible) +            if (cs.vco_divider == 1){ +                cs.vco_divider = least_divisor<size_t>(cs.chan_divider, 2); +                cs.chan_divider /= cs.vco_divider; +            } + +            if (CLOCK_SETTINGS_DEBUG){ +                std::cout << "gcd " << gcd << std::endl; +                std::cout << "X " << X << std::endl; +                std::cout << "Y " << Y << std::endl; +                std::cout << cs.to_pp_string() << std::endl; +            } + +            //filter limits on the counters +            if (cs.vco_divider == 1) continue; +            if (cs.r_counter >= (1<<14)) continue; +            if (cs.b_counter == 2) continue; +            if (cs.b_counter == 1 and cs.a_counter != 0) continue; +            if (cs.b_counter >= (1<<13)) continue; +            if (cs.a_counter >= (1<<6)) continue; + +            std::cout << "USRP-E100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl; +            return cs; +        } +    } + +    throw std::runtime_error(str(boost::format( +        "USRP-E100 clock control: could not calculate settings for clock rate %fMHz" +    ) % (rate/1e6))); +}  /***********************************************************************   * Clock Control Implementation @@ -62,35 +170,70 @@ class usrp_e100_clock_ctrl_impl : public usrp_e100_clock_ctrl{  public:      usrp_e100_clock_ctrl_impl(usrp_e100_iface::sptr iface){          _iface = iface; +        _chan_rate = 0.0; +        _out_rate = 0.0;          //init the clock gen registers          //Note: out0 should already be clocking the FPGA or this isnt going to work          _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO; -        _ad9522_regs.enable_clock_doubler = 1; //enable ref clock doubler          _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin          _ad9522_regs.status_pin_control = 0x1; //n divider          _ad9522_regs.ld_pin_control = 0x00; //dld          _ad9522_regs.refmon_pin_control = 0x12; //show ref2 +        _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC; -        _ad9522_regs.enable_ref2 = 1; -        _ad9522_regs.enable_ref1 = 0; -        _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2; +        this->use_internal_ref(); + +        this->set_fpga_clock_rate(DEFAULT_OUTPUT_RATE); //initialize to something + +        this->enable_test_clock(ENABLE_THE_TEST_OUT); +        this->enable_rx_dboard_clock(false); +        this->enable_tx_dboard_clock(false); +    } + +    ~usrp_e100_clock_ctrl_impl(void){ +        this->enable_test_clock(ENABLE_THE_TEST_OUT); +        this->enable_rx_dboard_clock(false); +        this->enable_tx_dboard_clock(false); +    } + +    /*********************************************************************** +     * Clock rate control: +     *  - set clock rate w/ internal VCO +     *  - set clock rate w/ external VCXO +     **********************************************************************/ +    void set_clock_settings_with_internal_vco(double rate){ +        const clock_settings_type cs = get_clock_settings(rate); + +        //set the rates to private variables so the implementation knows! +        _chan_rate = cs.get_chan_rate(); +        _out_rate = cs.get_out_rate(); -        _ad9522_regs.set_r_counter(r_counter); -        _ad9522_regs.a_counter = a_counter; -        _ad9522_regs.set_b_counter(b_counter); +        _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0; + +        _ad9522_regs.set_r_counter(cs.r_counter); +        _ad9522_regs.a_counter = cs.a_counter; +        _ad9522_regs.set_b_counter(cs.b_counter); +        UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below:          _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9;          _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;          _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; -        _ad9522_regs.vco_calibration_now = 1; //calibrate it! -        _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; +        _ad9522_regs.bypass_vco_divider = 0; +        switch(cs.vco_divider){ +        case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break; +        case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break; +        case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break; +        case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break; +        case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break; +        case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break; +        }          _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO;          //setup fpga master clock          _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; -        set_clock_divider(fpga_clock_divider, +        set_clock_divider(cs.chan_divider,              _ad9522_regs.divider0_low_cycles,              _ad9522_regs.divider0_high_cycles,              _ad9522_regs.divider0_bypass @@ -98,52 +241,69 @@ public:          //setup codec clock          _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; -        set_clock_divider(codec_clock_divider, +        set_clock_divider(cs.chan_divider,              _ad9522_regs.divider1_low_cycles,              _ad9522_regs.divider1_high_cycles,              _ad9522_regs.divider1_bypass          ); -        //setup test clock (same divider as codec clock) -        _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; -        _ad9522_regs.out4_cmos_configuration = (enable_test_clock)? -            ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : -            ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; +        this->send_all_regs(); +        calibrate_now(); +    } -        //setup a list of register ranges to write -        typedef std::pair<boost::uint16_t, boost::uint16_t> range_t; -        static const std::vector<range_t> ranges = boost::assign::list_of -            (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) -            (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) -            (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) -        ; +    void set_clock_settings_with_external_vcxo(double rate){ +        //set the rates to private variables so the implementation knows! +        _chan_rate = rate; +        _out_rate = rate; -        //write initial register values and latch/update -        BOOST_FOREACH(const range_t &range, ranges){ -            for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ -                this->send_reg(addr); -            } -        } -        this->latch_regs(); -        //test read: -        //boost::uint32_t reg = _ad9522_regs.get_read_reg(0x01b); -        //boost::uint32_t result = _iface->transact_spi( -        //    UE_SPI_SS_AD9522, -        //    spi_config_t::EDGE_RISE, -        //    reg, 24, true /*no*/ -        //); -        //std::cout << "result " << std::hex << result << std::endl; -        this->enable_rx_dboard_clock(false); -        this->enable_tx_dboard_clock(false); +        _ad9522_regs.enable_clock_doubler = 1; //doubler always on +        const double ref_rate = REFERENCE_INPUT_RATE*2; + +        //bypass prescaler such that N = B +        long gcd = boost::math::gcd(long(ref_rate), long(rate)); +        _ad9522_regs.set_r_counter(int(ref_rate/gcd)); +        _ad9522_regs.a_counter = 0; +        _ad9522_regs.set_b_counter(int(rate/gcd)); +        _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1; + +        //setup external vcxo +        _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; +        _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; +        _ad9522_regs.bypass_vco_divider = 1; +        _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL; + +        //setup fpga master clock +        _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; +        _ad9522_regs.divider0_bypass = 1; + +        //setup codec clock +        _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; +        _ad9522_regs.divider1_bypass = 1; + +        this->send_all_regs();      } -    ~usrp_e100_clock_ctrl_impl(void){ -        this->enable_rx_dboard_clock(false); -        this->enable_tx_dboard_clock(false); +    void set_fpga_clock_rate(double rate){ +        if (_out_rate == rate) return; +        if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate); +        else                 set_clock_settings_with_internal_vco(rate);      }      double get_fpga_clock_rate(void){ -        return master_clock_rate/fpga_clock_divider; +        return this->_out_rate; +    } + +    /*********************************************************************** +     * Special test clock output +     **********************************************************************/ +    void enable_test_clock(bool enb){ +        //setup test clock (same divider as codec clock) +        _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; +        _ad9522_regs.out4_cmos_configuration = (enb)? +            ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : +            ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; +        this->send_reg(0x0F0); +        this->latch_regs();      }      /*********************************************************************** @@ -161,13 +321,13 @@ public:      std::vector<double> get_rx_dboard_clock_rates(void){          std::vector<double> rates;          for(size_t div = 1; div <= 16+16; div++) -            rates.push_back(master_clock_rate/div); +            rates.push_back(this->_chan_rate/div);          return rates;      }      void set_rx_dboard_clock_rate(double rate){          assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate"); -        size_t divider = size_t(master_clock_rate/rate); +        size_t divider = size_t(this->_chan_rate/rate);          //set the divider registers          set_clock_divider(divider,              _ad9522_regs.divider3_low_cycles, @@ -197,7 +357,7 @@ public:      void set_tx_dboard_clock_rate(double rate){          assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate"); -        size_t divider = size_t(master_clock_rate/rate); +        size_t divider = size_t(this->_chan_rate/rate);          //set the divider registers          set_clock_divider(divider,              _ad9522_regs.divider2_low_cycles, @@ -238,6 +398,8 @@ public:  private:      usrp_e100_iface::sptr _iface;      ad9522_regs_t _ad9522_regs; +    double _out_rate; //rate at the fpga and codec +    double _chan_rate; //rate before final dividers      void latch_regs(void){          _ad9522_regs.io_update = 1; @@ -253,6 +415,46 @@ private:              reg, 24, false /*no rb*/          );      } + +    void calibrate_now(void){ +        //vco calibration routine: +        _ad9522_regs.vco_calibration_now = 0; +        this->send_reg(0x18); +        this->latch_regs(); +        _ad9522_regs.vco_calibration_now = 1; +        this->send_reg(0x18); +        this->latch_regs(); +        //wait for calibration done: +        static const boost::uint8_t addr = 0x01F; +        for (size_t ms10 = 0; ms10 < 100; ms10++){ +            boost::uint32_t reg = _iface->transact_spi( +                UE_SPI_SS_AD9522, spi_config_t::EDGE_RISE, +                _ad9522_regs.get_read_reg(addr), 24, true /*rb*/ +            ); +            _ad9522_regs.set_reg(addr, reg); +            if (_ad9522_regs.vco_calibration_finished) return; +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +        } +        std::cerr << "USRP-E100 clock control: VCO calibration timeout" << std::endl; +    } + +    void send_all_regs(void){ +        //setup a list of register ranges to write +        typedef std::pair<boost::uint16_t, boost::uint16_t> range_t; +        static const std::vector<range_t> ranges = boost::assign::list_of +            (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) +            (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) +            (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) +        ; + +        //write initial register values and latch/update +        BOOST_FOREACH(const range_t &range, ranges){ +            for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ +                this->send_reg(addr); +            } +        } +        this->latch_regs(); +    }  };  /*********************************************************************** diff --git a/host/lib/usrp/usrp_e100/clock_ctrl.hpp b/host/lib/usrp/usrp_e100/clock_ctrl.hpp index d613d1473..1f9960ce4 100644 --- a/host/lib/usrp/usrp_e100/clock_ctrl.hpp +++ b/host/lib/usrp/usrp_e100/clock_ctrl.hpp @@ -40,6 +40,13 @@ public:      static sptr make(usrp_e100_iface::sptr iface);      /*! +     * Set the rate of the fpga clock line. +     * Throws if rate is not valid. +     * \param rate the new rate in Hz +     */ +    virtual void set_fpga_clock_rate(double rate) = 0; + +    /*!       * Get the rate of the fpga clock line.       * \return the fpga clock rate in Hz       */ diff --git a/host/lib/usrp/usrp_e100/fpga-downloader.cc b/host/lib/usrp/usrp_e100/fpga_downloader.cpp index 4a3d3b9af..c0013fcbd 100644 --- a/host/lib/usrp/usrp_e100/fpga-downloader.cc +++ b/host/lib/usrp/usrp_e100/fpga_downloader.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -43,6 +43,8 @@   *  */ +namespace usrp_e_fpga_downloader_utility{ +  const unsigned int PROG_B = 175;  const unsigned int DONE   = 173;  const unsigned int INIT_B = 114; @@ -209,11 +211,10 @@ static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &d  {  	std::ifstream bitstream; -	std::cout << "File name - " << file_name.c_str() << std::endl; -  	bitstream.open(file_name.c_str(), std::ios::binary); -	if (!bitstream.is_open()) -		std::cout << "File " << file_name << " not opened succesfully." << std::endl; +	if (!bitstream.is_open()) throw std::runtime_error( +		"Coult not open the file: " + file_name +	);  	spidev spi("/dev/spidev1.0");  	char buf[BUF_SIZE]; @@ -232,35 +233,20 @@ static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &d  	} while (bitstream.gcount() == BUF_SIZE);  } -/* -int main(int argc, char *argv[]) -{ - -	gpio gpio_prog_b(PROG_B, OUT); -	gpio gpio_init_b(INIT_B, IN); -	gpio gpio_done  (DONE,   IN); - -	if (argc == 2) -		bit_file = argv[1]; - -	std::cout << "FPGA config file: " << bit_file << std::endl; - -	prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); - -	std::cout << "Done = " << gpio_done.get_value() << std::endl; - -	send_file_to_fpga(bit_file, gpio_init_b, gpio_done); -} -*/ +}//namespace usrp_e_fpga_downloader_utility  void usrp_e100_load_fpga(const std::string &bin_file){ +	using namespace usrp_e_fpga_downloader_utility; +  	gpio gpio_prog_b(PROG_B, OUT);  	gpio gpio_init_b(INIT_B, IN);  	gpio gpio_done  (DONE,   IN);  	std::cout << "Loading FPGA image: " << bin_file << "... " << std::flush; -	UHD_ASSERT_THROW(std::system("/sbin/rmmod usrp_e") == 0); +	if(std::system("/sbin/rmmod usrp_e") != 0){ +		std::cerr << "USRP-E100 FPGA downloader: could not unload usrp_e module" << std::endl; +	}  	prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); @@ -268,7 +254,9 @@ void usrp_e100_load_fpga(const std::string &bin_file){  	send_file_to_fpga(bin_file, gpio_init_b, gpio_done); -	UHD_ASSERT_THROW(std::system("/sbin/modprobe usrp_e") == 0); +	if(std::system("/sbin/modprobe usrp_e") != 0){ +		std::cerr << "USRP-E100 FPGA downloader: could not load usrp_e module" << std::endl; +	}  } diff --git a/host/lib/usrp/usrp_e100/io_impl.cpp b/host/lib/usrp/usrp_e100/io_impl.cpp index cf2410e4f..5fb2da7b8 100644 --- a/host/lib/usrp/usrp_e100/io_impl.cpp +++ b/host/lib/usrp/usrp_e100/io_impl.cpp @@ -35,7 +35,8 @@ zero_copy_if::sptr usrp_e100_make_mmap_zero_copy(usrp_e100_iface::sptr iface);  /***********************************************************************   * Constants   **********************************************************************/ -static const size_t tx_async_report_sid = 1; +static const size_t rx_data_inline_sid = 1; +static const size_t tx_async_report_sid = 2;  static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET;  static const bool recv_debug = false; @@ -109,8 +110,17 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(usrp_e100_clock_ctrl::sptr clock_              const boost::uint32_t *vrt_hdr = buff->cast<const boost::uint32_t *>();              vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); +            //handle an rx data packet or inline message +            if (if_packet_info.sid == rx_data_inline_sid){ +                if (recv_debug) std::cout << "this is rx_data_inline_sid\n"; +                //same number of frames as the data transport -> always immediate +                recv_pirate_booty.push_with_wait(buff); +                continue; +            } +              //handle a tx async report message              if (if_packet_info.sid == tx_async_report_sid and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ +                if (recv_debug) std::cout << "this is tx_async_report_sid\n";                  //fill in the async metadata                  async_metadata_t metadata; @@ -127,8 +137,7 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(usrp_e100_clock_ctrl::sptr clock_                  continue;              } -            //same number of frames as the data transport -> always immediate -            recv_pirate_booty.push_with_wait(buff); +            if (recv_debug) std::cout << "this is unknown packet\n";          }catch(const std::exception &e){              std::cerr << "Error (usrp-e recv pirate loop): " << e.what() << std::endl; @@ -152,17 +161,20 @@ void usrp_e100_impl::io_init(void){      //setup before the registers (transport called to calculate max spp)      _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface)); +    //clear state machines +    _iface->poke32(UE_REG_CTRL_RX_CLEAR, 0); +    _iface->poke32(UE_REG_CTRL_TX_CLEAR, 0); +      //setup rx data path      _iface->poke32(UE_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet());      _iface->poke32(UE_REG_CTRL_RX_NCHANNELS, 1); -    _iface->poke32(UE_REG_CTRL_RX_CLEAR_OVERRUN, 1); //reset      _iface->poke32(UE_REG_CTRL_RX_VRT_HEADER, 0          | (0x1 << 28) //if data with stream id          | (0x1 << 26) //has trailer          | (0x3 << 22) //integer time other          | (0x1 << 20) //fractional time sample count      ); -    _iface->poke32(UE_REG_CTRL_RX_VRT_STREAM_ID, 0); +    _iface->poke32(UE_REG_CTRL_RX_VRT_STREAM_ID, rx_data_inline_sid);      _iface->poke32(UE_REG_CTRL_RX_VRT_TRAILER, 0);      //setup the tx policy @@ -184,7 +196,6 @@ void usrp_e100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){  void usrp_e100_impl::handle_overrun(size_t){      std::cerr << "O"; //the famous OOOOOOOOOOO -    _iface->poke32(UE_REG_CTRL_RX_CLEAR_OVERRUN, 0);      if (_io_impl->continuous_streaming){          this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);      } diff --git a/host/lib/usrp/usrp_e100/mboard_impl.cpp b/host/lib/usrp/usrp_e100/mboard_impl.cpp index f52d2e6fb..0e08cd435 100644 --- a/host/lib/usrp/usrp_e100/mboard_impl.cpp +++ b/host/lib/usrp/usrp_e100/mboard_impl.cpp @@ -152,6 +152,10 @@ void usrp_e100_impl::mboard_get(const wax::obj &key_, wax::obj &val){          return;      } +    case MBOARD_PROP_CLOCK_RATE: +        val = _clock_ctrl->get_fpga_clock_rate(); +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -211,6 +215,10 @@ void usrp_e100_impl::mboard_set(const wax::obj &key, const wax::obj &val){          update_clock_config();          return; +    case MBOARD_PROP_CLOCK_RATE: +        _clock_ctrl->set_fpga_clock_rate(val.as<double>()); +        return; +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp b/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp index 40c7afabb..ad36dd97a 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@  //  #include "usrp_e100_iface.hpp" +#include "usrp_e100_regs.hpp"  #include <uhd/utils/assert.hpp>  #include <sys/ioctl.h> //ioctl  #include <fcntl.h> //open, close @@ -108,6 +109,9 @@ public:              throw std::runtime_error("Failed to open " + node);          } +        //very first thing, reset all the wishbone, always do first! +        this->poke32(UE_REG_CLEAR_GLOBAL, 0); +          mb_eeprom = mboard_eeprom_t(get_i2c_dev_iface(), mboard_eeprom_t::MAP_E100);      } diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp index 2dc401305..897616320 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp @@ -30,7 +30,7 @@  #ifndef INCLUDED_USRP_E100_IMPL_HPP  #define INCLUDED_USRP_E100_IMPL_HPP -static const boost::uint16_t USRP_E_COMPAT_NUM = 0x02; //make this 3 then the mainline fpga image gets fixed for embedded +static const boost::uint16_t USRP_E_COMPAT_NUM = 0x03;  //! load an fpga image from a bin file into the usrp-e fpga  extern void usrp_e100_load_fpga(const std::string &bin_file); diff --git a/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp b/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp index a57fe5171..a030462d0 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp @@ -17,7 +17,6 @@  // Slave pointers  #define UE_REG_SLAVE(n) ((n)<<7) -#define UE_REG_SR_ADDR(n) ((UE_REG_SLAVE(5)) + (4*(n)))  /////////////////////////////////////////////////////  // Slave 0 -- Misc Regs @@ -89,16 +88,6 @@  #define GPIO_SEL_DEBUG_0   0 // if pin is an output, debug lines from FPGA fabric  #define GPIO_SEL_DEBUG_1   1 // if pin is an output, debug lines from FPGA fabric - -//////////////////////////////////////////////////// -// Slave 5 -- Settings Bus -// -// Output-only, no readback, 32 registers total -//  Each register must be written 32 bits at a time -//  First the address xxx_xx00 and then xxx_xx10 - -#define UE_REG_SETTINGS_BASE UE_REG_SLAVE(5) -  ///////////////////////////////////////////////////  // Slave 6 -- ATR Controller  //   16 regs @@ -123,48 +112,72 @@  #define UE_REG_RB_TIME_NOW_TICKS  UE_REG_RB_MUX_32_BASE + 4  #define UE_REG_RB_TIME_PPS_SECS   UE_REG_RB_MUX_32_BASE + 8  #define UE_REG_RB_TIME_PPS_TICKS  UE_REG_RB_MUX_32_BASE + 12 +#define UE_REG_RB_MISC_TEST32     UE_REG_RB_MUX_32_BASE + 16 + +//////////////////////////////////////////////////// +// Slave 8 -- Settings Bus +// +// Output-only, no readback, 64 registers total +//  Each register must be written 64 bits at a time +//  First the address xxx_xx00 and then xxx_xx10 + +#define UE_REG_SETTINGS_BASE_ADDR(n) (UE_REG_SLAVE(8) + (4*(n))) + +#define UE_REG_SR_MISC_TEST32        UE_REG_SETTINGS_BASE_ADDR(52) + +///////////////////////////////////////////////// +// Magic reset regs +//////////////////////////////////////////////// +#define UE_REG_CLEAR_ADDR(n)      (UE_REG_SETTINGS_BASE_ADDR(48) + (4*(n))) +#define UE_REG_CLEAR_RX           UE_REG_CLEAR_ADDR(0) +#define UE_REG_CLEAR_TX           UE_REG_CLEAR_ADDR(1) +#define UE_REG_CLEAR_GLOBAL       UE_REG_CLEAR_ADDR(2)  /////////////////////////////////////////////////  // DSP RX Regs  //////////////////////////////////////////////// -#define UE_REG_DSP_RX_FREQ         UE_REG_SR_ADDR(0) -#define UE_REG_DSP_RX_SCALE_IQ     UE_REG_SR_ADDR(1)  // {scale_i,scale_q} -#define UE_REG_DSP_RX_DECIM_RATE   UE_REG_SR_ADDR(2)  // hb and decim rate -#define UE_REG_DSP_RX_DCOFFSET_I   UE_REG_SR_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic -#define UE_REG_DSP_RX_DCOFFSET_Q   UE_REG_SR_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits -#define UE_REG_DSP_RX_MUX          UE_REG_SR_ADDR(5) +#define UE_REG_DSP_RX_ADDR(n)      (UE_REG_SETTINGS_BASE_ADDR(16) + (4*(n))) +#define UE_REG_DSP_RX_FREQ         UE_REG_DSP_RX_ADDR(0) +#define UE_REG_DSP_RX_SCALE_IQ     UE_REG_DSP_RX_ADDR(1) // {scale_i,scale_q} +#define UE_REG_DSP_RX_DECIM_RATE   UE_REG_DSP_RX_ADDR(2) // hb and decim rate +#define UE_REG_DSP_RX_DCOFFSET_I   UE_REG_DSP_RX_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic +#define UE_REG_DSP_RX_DCOFFSET_Q   UE_REG_DSP_RX_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits +#define UE_REG_DSP_RX_MUX          UE_REG_DSP_RX_ADDR(5)  ///////////////////////////////////////////////////  // VITA RX CTRL regs  /////////////////////////////////////////////////// +#define UE_REG_CTRL_RX_ADDR(n)           (UE_REG_SETTINGS_BASE_ADDR(0) + (4*(n)))  // The following 3 are logically a single command register.  // They are clocked into the underlying fifo when time_ticks is written. -#define UE_REG_CTRL_RX_STREAM_CMD        UE_REG_SR_ADDR(8) // {now, chain, num_samples(30) -#define UE_REG_CTRL_RX_TIME_SECS         UE_REG_SR_ADDR(9) -#define UE_REG_CTRL_RX_TIME_TICKS        UE_REG_SR_ADDR(10) -#define UE_REG_CTRL_RX_CLEAR_OVERRUN     UE_REG_SR_ADDR(11) // write anything to clear overrun -#define UE_REG_CTRL_RX_VRT_HEADER        UE_REG_SR_ADDR(12) // word 0 of packet.  FPGA fills in packet counter -#define UE_REG_CTRL_RX_VRT_STREAM_ID     UE_REG_SR_ADDR(13) // word 1 of packet. -#define UE_REG_CTRL_RX_VRT_TRAILER       UE_REG_SR_ADDR(14) -#define UE_REG_CTRL_RX_NSAMPS_PER_PKT    UE_REG_SR_ADDR(15) -#define UE_REG_CTRL_RX_NCHANNELS         UE_REG_SR_ADDR(16) // 1 in basic case, up to 4 for vector sources +#define UE_REG_CTRL_RX_STREAM_CMD        UE_REG_CTRL_RX_ADDR(0) // {now, chain, num_samples(30) +#define UE_REG_CTRL_RX_TIME_SECS         UE_REG_CTRL_RX_ADDR(1) +#define UE_REG_CTRL_RX_TIME_TICKS        UE_REG_CTRL_RX_ADDR(2) +#define UE_REG_CTRL_RX_CLEAR             UE_REG_CTRL_RX_ADDR(3) // write anything to clear +#define UE_REG_CTRL_RX_VRT_HEADER        UE_REG_CTRL_RX_ADDR(4) // word 0 of packet.  FPGA fills in packet counter +#define UE_REG_CTRL_RX_VRT_STREAM_ID     UE_REG_CTRL_RX_ADDR(5) // word 1 of packet. +#define UE_REG_CTRL_RX_VRT_TRAILER       UE_REG_CTRL_RX_ADDR(6) +#define UE_REG_CTRL_RX_NSAMPS_PER_PKT    UE_REG_CTRL_RX_ADDR(7) +#define UE_REG_CTRL_RX_NCHANNELS         UE_REG_CTRL_RX_ADDR(8) // 1 in basic case, up to 4 for vector sources  /////////////////////////////////////////////////  // DSP TX Regs  //////////////////////////////////////////////// -#define UE_REG_DSP_TX_FREQ         UE_REG_SR_ADDR(17) -#define UE_REG_DSP_TX_SCALE_IQ     UE_REG_SR_ADDR(18) // {scale_i,scale_q} -#define UE_REG_DSP_TX_INTERP_RATE  UE_REG_SR_ADDR(19) -#define UE_REG_DSP_TX_UNUSED       UE_REG_SR_ADDR(20) -#define UE_REG_DSP_TX_MUX          UE_REG_SR_ADDR(21) +#define UE_REG_DSP_TX_ADDR(n)      (UE_REG_SETTINGS_BASE_ADDR(32) + (4*(n))) +#define UE_REG_DSP_TX_FREQ         UE_REG_DSP_TX_ADDR(0) +#define UE_REG_DSP_TX_SCALE_IQ     UE_REG_DSP_TX_ADDR(1) // {scale_i,scale_q} +#define UE_REG_DSP_TX_INTERP_RATE  UE_REG_DSP_TX_ADDR(2) +#define UE_REG_DSP_TX_UNUSED       UE_REG_DSP_TX_ADDR(3) +#define UE_REG_DSP_TX_MUX          UE_REG_DSP_TX_ADDR(4)  /////////////////////////////////////////////////  // VITA TX CTRL regs  //////////////////////////////////////////////// -#define UE_REG_CTRL_TX_NCHANNELS         UE_REG_SR_ADDR(24) -#define UE_REG_CTRL_TX_CLEAR_UNDERRUN    UE_REG_SR_ADDR(25) -#define UE_REG_CTRL_TX_REPORT_SID        UE_REG_SR_ADDR(26) -#define UE_REG_CTRL_TX_POLICY            UE_REG_SR_ADDR(27) +#define UE_REG_CTRL_TX_ADDR(n)           (UE_REG_SETTINGS_BASE_ADDR(24) + (4*(n))) +#define UE_REG_CTRL_TX_NCHANNELS         UE_REG_CTRL_TX_ADDR(0) +#define UE_REG_CTRL_TX_CLEAR             UE_REG_CTRL_TX_ADDR(1) +#define UE_REG_CTRL_TX_REPORT_SID        UE_REG_CTRL_TX_ADDR(2) +#define UE_REG_CTRL_TX_POLICY            UE_REG_CTRL_TX_ADDR(3)  #define UE_FLAG_CTRL_TX_POLICY_WAIT          (0x1 << 0)  #define UE_FLAG_CTRL_TX_POLICY_NEXT_PACKET   (0x1 << 1) @@ -189,11 +202,12 @@     *     * </pre>     */ -#define UE_REG_TIME64_SECS  UE_REG_SR_ADDR(28)  // value to set absolute secs to on next PPS -#define UE_REG_TIME64_TICKS UE_REG_SR_ADDR(29)  // value to set absolute ticks to on next PPS -#define UE_REG_TIME64_FLAGS UE_REG_SR_ADDR(30)  // flags - see chart above -#define UE_REG_TIME64_IMM   UE_REG_SR_ADDR(31)  // set immediate (0=latch on next pps, 1=latch immediate, default=0) -#define UE_REG_TIME64_TPS   UE_REG_SR_ADDR(31)  // clock ticks per second (counter rollover) +#define UE_REG_TIME64_ADDR(n)     (UE_REG_SETTINGS_BASE_ADDR(40) + (4*(n))) +#define UE_REG_TIME64_SECS        UE_REG_TIME64_ADDR(0)  // value to set absolute secs to on next PPS +#define UE_REG_TIME64_TICKS       UE_REG_TIME64_ADDR(1)  // value to set absolute ticks to on next PPS +#define UE_REG_TIME64_FLAGS       UE_REG_TIME64_ADDR(2)  // flags - see chart above +#define UE_REG_TIME64_IMM         UE_REG_TIME64_ADDR(3)  // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define UE_REG_TIME64_TPS         UE_REG_TIME64_ADDR(4)  // clock ticks per second (counter rollover)  //pps flags (see above)  #define UE_FLAG_TIME64_PPS_NEGEDGE (0 << 0) diff --git a/host/usrp_e_utils/CMakeLists.txt b/host/usrp_e_utils/CMakeLists.txt new file mode 100644 index 000000000..5123af592 --- /dev/null +++ b/host/usrp_e_utils/CMakeLists.txt @@ -0,0 +1,51 @@ +# +# Copyright 2011 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# USRP embedded utilities that get installed into the share path +######################################################################## +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") +    SET(LINUX_TARGET TRUE) +ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + +LIBUHD_REGISTER_COMPONENT("USRP-E Utils" ENABLE_USRP_E_UTILS OFF "LINUX_TARGET" OFF) + +IF(ENABLE_USRP_E_UTILS) +    ENABLE_LANGUAGE(C) +    INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e100) +    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e100/include) +    INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}/lib/ic_reg_maps) + +    SET(usrp_e_utils_sources +        usrp-e-utility.cpp +        usrp-e-loopback.c +        usrp-e-wb-test.cpp +        usrp-e-debug-pins.c +        usrp-e-i2c.c +        usrp-e-spi.c +    ) + +    #for each source: build an executable and install +    FOREACH(util_source ${usrp_e_utils_sources}) +        GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE) +        ADD_EXECUTABLE(${util_name} ${util_source}) +        TARGET_LINK_LIBRARIES(${util_name} ${Boost_LIBRARIES}) +        INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_DATA_DIR}/usrp_e_utils) +    ENDFOREACH(util_source) + +ENDIF(ENABLE_USRP_E_UTILS) diff --git a/host/utils/clkgen-config.cpp b/host/usrp_e_utils/clkgen_config.hpp index e8279b4ae..f39f8bb19 100644 --- a/host/utils/clkgen-config.cpp +++ b/host/usrp_e_utils/clkgen_config.hpp @@ -1,24 +1,22 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2004,2008,2009 Free Software Foundation, Inc. - * - * This file is part of UHD - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING.  If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. -*/ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef USRP_E_UTILS_CLKGEN_CONFIG_HPP +#define USRP_E_UTILS_CLKGEN_CONFIG_HPP  #include <iostream>  #include <sstream> @@ -33,6 +31,7 @@  #include <linux/spi/spidev.h> +namespace usrp_e_clkgen_config_utility{  // Programming data for clock gen chip  static const unsigned int config_data[] = { @@ -73,7 +72,7 @@ static const unsigned int config_data[] = {  	0x019021,  	0x019100,  	0x019200, -	0x019333, +	0x019321,  	0x019400,  	0x019500,  	0x019611, @@ -157,6 +156,7 @@ class spidev {  };  gpio::gpio(unsigned int _gpio_num, gpio_direction pin_direction, bool close_action) +:close_action(close_action)  {  	std::fstream export_file; @@ -264,10 +264,10 @@ void spidev::send(char *buf, char *rbuf, unsigned int nbytes)  	tr.rx_buf = (unsigned long) rbuf;  	tr.len = nbytes;  	tr.delay_usecs = 0; -	tr.speed_hz = 12000000; +	tr.speed_hz = 12000;  	tr.bits_per_word = 24; -	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);	 +	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);  } @@ -278,19 +278,28 @@ static void send_config_to_clkgen(gpio &chip_select, const unsigned int data[],  	for (unsigned int i = 0; i < data_size; i++) { -		std::cout << "sending " << std::hex << data[i] << std::endl; +		//std::cout << "sending " << std::hex << data[i] << std::endl;  		chip_select.set_value(0);  		spi.send((char *)&data[i], (char *)&rbuf, 4);  		chip_select.set_value(1); - +		unsigned int addr = (data[i] >> 8) & 0xfff; +		if (addr == 0x232 || addr == 0x000){ +			std::cout << "." << std::flush; +			sleep(1); +		}  	}; +	std::cout << std::endl;  } -int main(int argc, char *argv[]) -{ +}//namespace usrp_e_clkgen_config_utility +//int main(int argc, char *argv[]) +static void clock_genconfig_main(void) +{ +	using namespace usrp_e_clkgen_config_utility;  	gpio clkgen_select(CLKGEN_SELECT, OUT, true); -	send_config_to_clkgen(clkgen_select, config_data, sizeof(config_data)/sizeof(unsigned int)); +	send_config_to_clkgen(clkgen_select, config_data, sizeof(config_data)/sizeof(config_data[0]));  } +#endif /*USRP_E_UTILS_CLKGEN_CONFIG_HPP*/ diff --git a/host/utils/usrp-e-debug-pins.c b/host/usrp_e_utils/usrp-e-debug-pins.c index 94f898b67..94f898b67 100644 --- a/host/utils/usrp-e-debug-pins.c +++ b/host/usrp_e_utils/usrp-e-debug-pins.c diff --git a/host/utils/usrp-e-i2c.c b/host/usrp_e_utils/usrp-e-i2c.c index c6fd4c632..c6fd4c632 100644 --- a/host/utils/usrp-e-i2c.c +++ b/host/usrp_e_utils/usrp-e-i2c.c diff --git a/host/utils/usrp-e-loopback.c b/host/usrp_e_utils/usrp-e-loopback.c index 454d81ba7..454d81ba7 100644 --- a/host/utils/usrp-e-loopback.c +++ b/host/usrp_e_utils/usrp-e-loopback.c diff --git a/host/utils/usrp-e-spi.c b/host/usrp_e_utils/usrp-e-spi.c index 5203f56a8..5203f56a8 100644 --- a/host/utils/usrp-e-spi.c +++ b/host/usrp_e_utils/usrp-e-spi.c diff --git a/host/usrp_e_utils/usrp-e-utility.cpp b/host/usrp_e_utils/usrp-e-utility.cpp new file mode 100644 index 000000000..b926cf49d --- /dev/null +++ b/host/usrp_e_utils/usrp-e-utility.cpp @@ -0,0 +1,75 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/safe_main.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <stdexcept> +#include <iostream> + +#include "fpga_downloader.cpp" +#include "clkgen_config.hpp" + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + +    //variables to be set by po +    std::string fpga_path; + +    //setup the program options +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("fpga", po::value<std::string>(&fpga_path), "loads the specified FPGA file") +        ("reclk",                                    "runs the clock recovery") +    ; +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm); + +    //print the help message +    if (vm.count("help")){ +        std::cout << boost::format("UHD USRP-E Utility %s") % desc << std::endl; +        return ~0; +    } + +    bool loaded_fpga_image = false; +    if (vm.count("fpga") != 0){ +        std::cout << "USRP-E Utility loading the FPGA..." << std::endl << std::endl; +        usrp_e100_load_fpga(fpga_path); +        loaded_fpga_image = true; +        sleep(1); +    } + +    if (vm.count("reclk") != 0){ +        std::cout << "USRP-E Utility running the clock recovery..." << std::flush; +        //if an image was not loaded or specified, we load pass-through +        if (fpga_path.empty()) throw std::runtime_error( +            "Please specify the path to the pass-though FPGA image for your device.\n" +            "  usrp-e-utility --reclk --fpga=/usr/share/uhd/images/usrp_e1xx_pt_fpga.bin" +        ); +        clock_genconfig_main(); +        if (std::system("rm /tmp/usrp*hash") != 0){ //clear hash so driver must reload +            std::cerr << "No hash to remove! Don't worry, its not a problem." << std::endl; +        } +    } + +    std::cout << "Done!" << std::endl; + +    return 0; +} diff --git a/host/usrp_e_utils/usrp-e-wb-test.cpp b/host/usrp_e_utils/usrp-e-wb-test.cpp new file mode 100644 index 000000000..3d6a8d101 --- /dev/null +++ b/host/usrp_e_utils/usrp-e-wb-test.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <cstdlib> +#include <cstdio> +#include <ctime> +#include <iostream> + +#include <sys/ioctl.h> //ioctl +#include <fcntl.h> //open, close + +#include <linux/usrp_e.h> +#include "usrp_e100_regs.hpp" + +static const size_t num_test_iters = 10000000; + +static int fp; + +static int peek16(int reg){ +    int ret; +    struct usrp_e_ctl16 d; + +    d.offset = reg; +    d.count = 1; +    ret = ioctl(fp, USRP_E_READ_CTL16, &d); +    return d.buf[0]; +} + +static void poke16(int reg, int val){ +    int ret; +    struct usrp_e_ctl16 d; + +    d.offset = reg; +    d.count = 1; +    d.buf[0] = val; +    ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); +} + +static int peek32(int reg){ +    int ret; +    struct usrp_e_ctl32 d; + +    d.offset = reg; +    d.count = 1; +    ret = ioctl(fp, USRP_E_READ_CTL32, &d); +    return d.buf[0]; +} + +static void poke32(int reg, int val){ +    int ret; +    struct usrp_e_ctl32 d; + +    d.offset = reg; +    d.count = 1; +    d.buf[0] = val; +    ret = ioctl(fp, USRP_E_WRITE_CTL32, &d); +} + +int main(int, char *[]){ + +    srandom(time(NULL)); //seed random() + +    if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ +        std::cerr << "Open failed" << std::endl; +        return -1; +    } + +    size_t num_pass = 0, num_fail = 0; +    for (size_t i = 0; i < num_test_iters; i++){ +	if(i%1000000 == 0) { +	    std::cout << "num pass: " << num_pass; +	    std::cout << "\tnum fail: " << num_fail << std::endl; +	} +        //make random values +        int random_test32 = ::random(); +        int random_test16 = ::random() & 0xffff; +        int random_secs = ::random(); + +        //set a bunch of registers +        poke16(UE_REG_MISC_TEST, random_test16); +        poke32(UE_REG_SR_MISC_TEST32, random_test32); +        poke32(UE_REG_TIME64_TICKS, 0); +        poke32(UE_REG_TIME64_IMM, 1); //immediate +        poke32(UE_REG_TIME64_SECS, random_secs); + +        //read a bunch of registers +        if ( +            (peek16(UE_REG_MISC_TEST) == random_test16) and +            (peek32(UE_REG_RB_MISC_TEST32) == random_test32) and +            (peek32(UE_REG_RB_TIME_NOW_SECS) == random_secs) and +//            (peek32(UE_REG_RB_TIME_NOW_TICKS) < 1000000) and +        true) num_pass++; +        else  num_fail++; +    } + +    std::cout << "num pass: " << num_pass << std::endl; +    std::cout << "num fail: " << num_fail << std::endl; + +    ::close(fp); +    return 0; +} diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 2df1c3529..53527c03d 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2010 Ettus Research LLC +# Copyright 2010-2011 Ettus Research LLC  #  # This program is free software: you can redistribute it and/or modify  # it under the terms of the GNU General Public License as published by @@ -45,20 +45,6 @@ IF(ENABLE_USRP1)      )  ENDIF(ENABLE_USRP1) -IF(ENABLE_USRP_E100) -    ENABLE_LANGUAGE(C) -    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e100) -    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e100/include) -    LIST(APPEND util_share_sources -        fpga-downloader.cpp -        clkgen-config.cpp -        usrp-e-loopback.c -        usrp-e-debug-pins.c -        usrp-e-i2c.c -        usrp-e-spi.c -    ) -ENDIF(ENABLE_USRP_E100) -  #for each source: build an executable and install  FOREACH(util_source ${util_share_sources})      GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE) diff --git a/host/utils/fpga-downloader.cpp b/host/utils/fpga-downloader.cpp deleted file mode 100644 index 80ee71600..000000000 --- a/host/utils/fpga-downloader.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2004,2008,2009 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING.  If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. -*/ - -#include <iostream> -#include <sstream> -#include <fstream> -#include <string> -#include <cstdlib> - -#include <fcntl.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> - -#include <linux/spi/spidev.h> - -/* - * Configuration connections - * - * CCK    - MCSPI1_CLK - * DIN    - MCSPI1_MOSI - * PROG_B - GPIO_175     - output (change mux) - * DONE   - GPIO_173     - input  (change mux) - * INIT_B - GPIO_114     - input  (change mux) - * -*/ - -const unsigned int PROG_B = 175; -const unsigned int DONE   = 173; -const unsigned int INIT_B = 114; - -static std::string bit_file = "safe_u1e.bin"; - -const int BUF_SIZE = 4096; - -enum gpio_direction {IN, OUT}; - -class gpio { -	public: - -	gpio(unsigned int gpio_num, gpio_direction pin_direction); - -	bool get_value(); -	void set_value(bool state); - -	private: - -	std::stringstream base_path; -	std::fstream value_file;	 -}; - -class spidev { -	public: - -	spidev(std::string dev_name); -	~spidev(); - -	void send(char *wbuf, char *rbuf, unsigned int nbytes); - -	private: - -	int fd; - -}; - -gpio::gpio(unsigned int gpio_num, gpio_direction pin_direction) -{ -	std::fstream export_file; - -	export_file.open("/sys/class/gpio/export", std::ios::out); -	if (!export_file.is_open())  ///\todo Poor error handling -		std::cout << "Failed to open gpio export file." << std::endl; - -	export_file << gpio_num << std::endl; - -	base_path << "/sys/class/gpio/gpio" << gpio_num << std::flush; - -	std::fstream direction_file; -	std::string direction_file_name; - -	direction_file_name = base_path.str() + "/direction"; - -	direction_file.open(direction_file_name.c_str());  -	if (!direction_file.is_open()) -		std::cout << "Failed to open direction file." << std::endl; -	if (pin_direction == OUT) -		direction_file << "out" << std::endl; -	else -		direction_file << "in" << std::endl; - -	std::string value_file_name; - -	value_file_name = base_path.str() + "/value"; - -	value_file.open(value_file_name.c_str(), std::ios_base::in | std::ios_base::out); -	if (!value_file.is_open()) -		std::cout << "Failed to open value file." << std::endl; -} - -bool gpio::get_value() -{ - -	std::string val; - -	std::getline(value_file, val); -	value_file.seekg(0); - -	if (val == "0") -		return false; -	else if (val == "1") -		return true; -	else -		std::cout << "Data read from value file|" << val << "|" << std::endl; - -	return false; -} - -void gpio::set_value(bool state) -{ - -	if (state) -		value_file << "1" << std::endl; -	else -		value_file << "0" << std::endl; -} - -static void prepare_fpga_for_configuration(gpio &prog, gpio &init) -{ - -	prog.set_value(true); -	prog.set_value(false); -	prog.set_value(true); - -#if 0 -	bool ready_to_program(false); -	unsigned int count(0); -	do { -		ready_to_program = init.get_value(); -		count++; - -		sleep(1); -	} while (count < 10 && !ready_to_program); - -	if (count == 10) { -		std::cout << "FPGA not ready for programming." << std::endl; -		exit(-1); -	} -#endif -} - -spidev::spidev(std::string fname) -{ -	int ret; -	int mode = 0; -	int speed = 12000000; -	int bits = 8; - -	fd = open(fname.c_str(), O_RDWR); - -	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); -	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); -	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); -} -	 - -spidev::~spidev() -{ -	close(fd); -} - -void spidev::send(char *buf, char *rbuf, unsigned int nbytes) -{ -	int ret; - -	struct spi_ioc_transfer tr; -	tr.tx_buf = (unsigned long) buf; -	tr.rx_buf = (unsigned long) rbuf; -	tr.len = nbytes; -	tr.delay_usecs = 0; -	tr.speed_hz = 48000000; -	tr.bits_per_word = 8; - -	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);	 - -} - -static void send_file_to_fpga(std::string &file_name, gpio &error, gpio &done) -{ -	std::ifstream bitstream; - -	std::cout << "File name - " << file_name.c_str() << std::endl; - -	bitstream.open(file_name.c_str(), std::ios::binary); -	if (!bitstream.is_open()) -		std::cout << "File " << file_name << " not opened succesfully." << std::endl; - -	spidev spi("/dev/spidev1.0"); -	char buf[BUF_SIZE]; -	char rbuf[BUF_SIZE]; - -	do { -		bitstream.read(buf, BUF_SIZE); -		spi.send(buf, rbuf, bitstream.gcount()); - -		if (error.get_value()) -			std::cout << "INIT_B went high, error occured." << std::endl; - -		if (!done.get_value()) -			std::cout << "Configuration complete." << std::endl; - -	} while (bitstream.gcount() == BUF_SIZE); -} - -int main(int argc, char *argv[]) -{ - -	gpio gpio_prog_b(PROG_B, OUT); -	gpio gpio_init_b(INIT_B, IN); -	gpio gpio_done  (DONE,   IN); - -	if (argc == 2) -		bit_file = argv[1]; - -	bool module_found(false); -	std::ifstream mod_file("/proc/modules"); -	while (!mod_file.eof()) { -		std::string line; -		getline(mod_file, line); -		if (line.find("usrp_e") != std::string::npos) -			module_found = true; -	} -	mod_file.close(); - -	if (module_found) { -		std::cout << "USRP Embedded kernel module loaded, not loading FPGA." << std::endl; -		return -1; -	} - -	std::cout << "FPGA config file: " << bit_file << std::endl; - -	prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); - -	std::cout << "Done = " << gpio_done.get_value() << std::endl; - -	send_file_to_fpga(bit_file, gpio_init_b, gpio_done); -} - diff --git a/images/Makefile b/images/Makefile index 34c18cdc2..cfc783ee4 100644 --- a/images/Makefile +++ b/images/Makefile @@ -152,6 +152,22 @@ $(_usrp_e100_fpga_bin): $(GLOBAL_DEPS)  endif  ######################################################################## +# USRP-E100 pass-through fpga +######################################################################## +ifdef HAS_XTCLSH + +_usrp_e100_pt_fpga_dir = $(TOP_FPGA_DIR)/usrp2/top/u1e_passthru +_usrp_e100_pt_fpga_bin = $(BUILT_IMAGES_DIR)/usrp_e100_pt_fpga.bin +IMAGES_LIST += $(_usrp_e100_pt_fpga_bin) + +$(_usrp_e100_pt_fpga_bin): $(GLOBAL_DEPS) +	cd $(_usrp_e100_pt_fpga_dir) && make clean +	cd $(_usrp_e100_pt_fpga_dir) && make bin +	cp $(_usrp_e100_pt_fpga_dir)/build/passthru.bin $@ + +endif + +########################################################################  # Build rules  ########################################################################  images: $(IMAGES_LIST) | 
