diff options
223 files changed, 13285 insertions, 6595 deletions
diff --git a/firmware/fx2/b100/usb_descriptors.a51 b/firmware/fx2/b100/usb_descriptors.a51 index 6efeb8367..847face16 100644 --- a/firmware/fx2/b100/usb_descriptors.a51 +++ b/firmware/fx2/b100/usb_descriptors.a51 @@ -211,8 +211,8 @@ _high_speed_config_descr:: .db DSCR_ENDPNT .db 0x04 ; bEndpointAddress (ep 4 OUT) .db ET_BULK ; bmAttributes - .db <32 ; wMaxPacketSize (LSB) - .db >32 ; wMaxPacketSize (MSB) + .db <512 ; wMaxPacketSize (LSB) + .db >512 ; wMaxPacketSize (MSB) .db 0 ; bInterval (iso only) ;; interface descriptor 4 (FPGA command IN path, ep8 IN BULK) @@ -233,8 +233,8 @@ _high_speed_config_descr:: .db DSCR_ENDPNT .db 0x88 ; bEndpointAddress (ep 8 IN) .db ET_BULK ; bmAttributes - .db <32 ; wMaxPacketSize (LSB) - .db >32 ; wMaxPacketSize (MSB) + .db <512 ; wMaxPacketSize (LSB) + .db >512 ; wMaxPacketSize (MSB) .db 0 ; bInterval (iso only) @@ -341,25 +341,24 @@ str0_end: .even str1: .db str1_end - str1 .db DSCR_STRING - .db 'F, 0 ; 16-bit unicode - .db 'r, 0 + .db 'E, 0 ; 16-bit unicode + .db 't, 0 + .db 't, 0 + .db 'u, 0 + .db 's, 0 + .db ' , 0 + .db 'R, 0 .db 'e, 0 + .db 's, 0 .db 'e, 0 - .db ' , 0 - .db 'S, 0 - .db 'o, 0 - .db 'f, 0 - .db 't, 0 - .db 'w, 0 .db 'a, 0 .db 'r, 0 - .db 'e, 0 + .db 'c, 0 + .db 'h, 0 .db ' , 0 - .db 'F, 0 - .db 'o, 0 - .db 'l, 0 - .db 'k, 0 - .db 's, 0 + .db 'L, 0 + .db 'L, 0 + .db 'C, 0 str1_end: SI_PRODUCT = 2 @@ -370,13 +369,12 @@ str2: .db str2_end - str2 .db 'S, 0 .db 'R, 0 .db 'P, 0 - .db '1, 0 - .db 'P, 0 - .db ' , 0 - .db 'R, 0 - .db 'e, 0 - .db 'v, 0 - .db ' , 0 + .db ' , 0 + .db 'B, 0 + .db '1, 0 + .db '0, 0 + .db '0, 0 + _usb_desc_hw_rev_ascii_patch_location_0:: .db '?, 0 str2_end: diff --git a/firmware/fx2/b100/usrp_common.c b/firmware/fx2/b100/usrp_common.c index a21353688..968b03d48 100644 --- a/firmware/fx2/b100/usrp_common.c +++ b/firmware/fx2/b100/usrp_common.c @@ -85,8 +85,8 @@ init_usrp (void) // set autoin length for EP6/EP8 EP6AUTOINLENH = (512) >> 8; SYNCDELAY; // this is the length for high speed EP6AUTOINLENL = (512) & 0xff; SYNCDELAY; - EP8AUTOINLENH = (32) >> 8; SYNCDELAY; - EP8AUTOINLENL = (32) & 0xff; SYNCDELAY; + EP8AUTOINLENH = (512) >> 8; SYNCDELAY; + EP8AUTOINLENL = (512) & 0xff; SYNCDELAY; //set FLAGA, FLAGB, FLAGC, FLAGD to be EP2EF, EP4EF, EP6PF, EP8PF PINFLAGSAB = (bmEP2EF) | (bmEP4EF << 4); @@ -99,13 +99,19 @@ init_usrp (void) EP6FIFOPFL = 0xFD; SYNCDELAY; + EP8FIFOPFH = 0x09; + SYNCDELAY; + EP8FIFOPFL = 0xFD; + SYNCDELAY; + // EP2FIFOPFH = 0x08; // SYNCDELAY; // EP2FIFOPFL = 0x00; // SYNCDELAY; //assert FIFOEMPTY one cycle sooner so we get it in time at the FPGA - EP2FIFOCFG |= bmBIT5; + EP2FIFOCFG |= bmBIT5; + EP4FIFOCFG |= bmBIT5; //set FIFOPINPOLAR to normal (active low) mode FIFOPINPOLAR = 0x00; diff --git a/firmware/fx2/b100/usrp_main.c b/firmware/fx2/b100/usrp_main.c index 74427b7d2..f79d3d111 100644 --- a/firmware/fx2/b100/usrp_main.c +++ b/firmware/fx2/b100/usrp_main.c @@ -124,7 +124,7 @@ app_vendor_cmd (void) case VRQ_FW_COMPAT: EP0BCH = 0; - EP0BCL = 3; + EP0BCL = 4; break; default: diff --git a/firmware/zpu/apps/txrx_uhd.c b/firmware/zpu/apps/txrx_uhd.c index 9daf441e2..30efc9518 100644 --- a/firmware/zpu/apps/txrx_uhd.c +++ b/firmware/zpu/apps/txrx_uhd.c @@ -89,11 +89,42 @@ static void handle_udp_data_packet( which = 1; break; + case USRP2_UDP_FIFO_CRTL_PORT: + which = 3; + break; + default: return; } - eth_mac_addr_t eth_mac_host; arp_cache_lookup_mac(&src.addr, ð_mac_host); - setup_framer(eth_mac_host, *ethernet_mac_addr(), src, dst, which); + //assume the packet destination is the packet source + //the arp cache lookup should never fail for this case + const struct socket_address src_addr = dst; + struct socket_address dst_addr = src; + eth_mac_addr_t eth_mac_dst; + arp_cache_lookup_mac(&dst_addr.addr, ð_mac_dst); + + //however, if this control packet has an alternative destination... + if (payload_len >= sizeof(usrp2_stream_ctrl_t)){ + + //parse the destination ip addr and udp port from the payload + const usrp2_stream_ctrl_t *stream_ctrl = (const usrp2_stream_ctrl_t *)payload; + dst_addr.addr.addr = stream_ctrl->ip_addr; + dst_addr.port = (uint16_t)stream_ctrl->udp_port; + struct ip_addr ip_dest = dst_addr.addr; + + //are we in the subnet? if not use the gateway + const uint32_t subnet_mask = get_subnet()->addr; + const bool in_subnet = ((get_ip_addr()->addr & subnet_mask) == (ip_dest.addr & subnet_mask)); + if (!in_subnet) ip_dest = *get_gateway(); + + //lookup the host ip address with ARP (this may fail) + const bool ok = arp_cache_lookup_mac(&ip_dest, ð_mac_dst); + if (!ok) net_common_send_arp_request(&ip_dest); + const uint32_t result = (ok)? 0 : ~0; + send_udp_pkt(dst.port, src, &result, sizeof(result)); + } + + setup_framer(eth_mac_dst, *ethernet_mac_addr(), dst_addr, src_addr, which); } #define OTW_GPIO_BANK_TO_NUM(bank) \ @@ -150,8 +181,8 @@ static void handle_udp_ctrl_packet( ctrl_data_in->data.spi_args.dev, //which device ctrl_data_in->data.spi_args.data, //32 bit data ctrl_data_in->data.spi_args.num_bits, //length in bits - (ctrl_data_in->data.spi_args.mosi_edge == USRP2_CLK_EDGE_RISE)? SPIF_PUSH_FALL : SPIF_PUSH_RISE | - (ctrl_data_in->data.spi_args.miso_edge == USRP2_CLK_EDGE_RISE)? SPIF_LATCH_RISE : SPIF_LATCH_FALL + ((ctrl_data_in->data.spi_args.mosi_edge == USRP2_CLK_EDGE_RISE)? SPI_PUSH_FALL : SPI_PUSH_RISE) | + ((ctrl_data_in->data.spi_args.miso_edge == USRP2_CLK_EDGE_RISE)? SPI_LATCH_RISE : SPI_LATCH_FALL) ); //load output @@ -279,6 +310,7 @@ int main(void) { u2_init(); + arp_cache_init(); #ifdef BOOTLOADER putstr("\nUSRP N210 UDP bootloader\n"); #else @@ -294,8 +326,7 @@ main(void) //load the production FPGA image or firmware if appropriate do_the_bootload_thing(); //if we get here we've fallen through to safe firmware - set_default_mac_addr(); - set_default_ip_addr(); + eth_addrs_set_default(); #endif print_mac_addr(ethernet_mac_addr()); newline(); @@ -311,6 +342,7 @@ main(void) register_udp_listener(USRP2_UDP_RX_DSP0_PORT, handle_udp_data_packet); register_udp_listener(USRP2_UDP_RX_DSP1_PORT, handle_udp_data_packet); register_udp_listener(USRP2_UDP_TX_DSP0_PORT, handle_udp_data_packet); + register_udp_listener(USRP2_UDP_FIFO_CRTL_PORT, handle_udp_data_packet); #ifdef USRP2P register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet); diff --git a/firmware/zpu/lib/ad9510.c b/firmware/zpu/lib/ad9510.c index 4d3acb65d..4021a9bf7 100644 --- a/firmware/zpu/lib/ad9510.c +++ b/firmware/zpu/lib/ad9510.c @@ -1,5 +1,5 @@ -/* -*- c++ -*- */ /* + * Copyright 2012 Ettus Research LLC * Copyright 2008 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@ ad9510_write_reg(int regno, uint8_t value) { uint32_t inst = WR | (regno & 0xff); uint32_t v = (inst << 8) | (value & 0xff); - spi_transact(SPI_TXONLY, SPI_SS_AD9510, v, 24, SPIF_PUSH_FALL); + spi_transact(SPI_TXONLY, SPI_SS_AD9510, v, 24, SPI_PUSH_FALL); } int @@ -37,6 +37,6 @@ ad9510_read_reg(int regno) uint32_t inst = RD | (regno & 0xff); uint32_t v = (inst << 8) | 0; uint32_t r = spi_transact(SPI_TXRX, SPI_SS_AD9510, v, 24, - SPIF_PUSH_FALL | SPIF_LATCH_FALL); + SPI_PUSH_FALL | SPI_LATCH_FALL); return r & 0xff; } diff --git a/firmware/zpu/lib/clocks.c b/firmware/zpu/lib/clocks.c index c1e8ce827..bc1954e13 100644 --- a/firmware/zpu/lib/clocks.c +++ b/firmware/zpu/lib/clocks.c @@ -43,7 +43,10 @@ clocks_init(void) //enable the 100MHz clock output to the FPGA for 50MHz CPU clock clocks_enable_fpga_clk(true, 1); - spi_wait(); + //! Cannot SPI wait since SPI is on DSP clock + //! because DSP clock goes away until DCM reset. + //! However, spi is quick, the cpu is slow, its already ready... + //spi_wait(); //wait for the clock to stabilize while(!clocks_lock_detect()); diff --git a/firmware/zpu/lib/eth_addrs.c b/firmware/zpu/lib/eth_addrs.c index c45ce7559..6d3347cf3 100644 --- a/firmware/zpu/lib/eth_addrs.c +++ b/firmware/zpu/lib/eth_addrs.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 Ettus Research LLC + * Copyright 2010-2012 Ettus Research LLC * Copyright 2007 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "memory_map.h" #include "nonstdio.h" #include <stdbool.h> +#include <string.h> #include "i2c.h" #include "usrp2/fw_common.h" @@ -37,104 +38,69 @@ unprogrammed(const void *t, size_t len) return all_ones | all_zeros; } -//////////////////// MAC Addr Stuff /////////////////////// +typedef struct{ + eth_mac_addr_t mac_addr; + struct ip_addr ip_addr; + struct ip_addr gateway; + struct ip_addr subnet; +} eth_addrs_t; -static bool src_mac_addr_initialized = false; +static bool eth_addrs_initialized = false; -static const eth_mac_addr_t default_mac_addr = {{ - 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff - }}; +static const eth_addrs_t default_eth_addrs = { + .mac_addr = {{0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff}}, + .ip_addr = {(192 << 24 | 168 << 16 | 10 << 8 | 2 << 0)}, + .gateway = {(192 << 24 | 168 << 16 | 10 << 8 | 1 << 0)}, + .subnet = {(255 << 24 | 255 << 16 | 255 << 8 | 0 << 0)}, +}; -static eth_mac_addr_t src_mac_addr = {{ - 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff - }}; - -void set_default_mac_addr(void) -{ - src_mac_addr_initialized = true; - src_mac_addr = default_mac_addr; -} +static eth_addrs_t current_eth_addrs; -const eth_mac_addr_t * -ethernet_mac_addr(void) -{ - if (!src_mac_addr_initialized){ // fetch from eeprom - src_mac_addr_initialized = true; +static void eth_addrs_init(void){ + if (eth_addrs_initialized) return; + eth_addrs_initialized = true; - // if we're simulating, don't read the EEPROM model, it's REALLY slow - if (hwconfig_simulation_p()) - return &src_mac_addr; - - eth_mac_addr_t tmp; - bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, &tmp, sizeof(tmp)); - if (!ok || unprogrammed(&tmp, sizeof(tmp))){ - // use the default + #define eth_addrs_init_x(addr, x){ \ + const bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, addr, ¤t_eth_addrs.x, sizeof(current_eth_addrs.x)); \ + if (!ok || unprogrammed(¤t_eth_addrs.x, sizeof(current_eth_addrs.x))){ \ + memcpy(¤t_eth_addrs.x, &default_eth_addrs.x, sizeof(current_eth_addrs.x)); \ + } \ } - else - src_mac_addr = tmp; - } - return &src_mac_addr; -} + eth_addrs_init_x(USRP2_EE_MBOARD_MAC_ADDR, mac_addr); + eth_addrs_init_x(USRP2_EE_MBOARD_IP_ADDR, ip_addr); + eth_addrs_init_x(USRP2_EE_MBOARD_GATEWAY, gateway); + eth_addrs_init_x(USRP2_EE_MBOARD_SUBNET, subnet); -bool -ethernet_set_mac_addr(const eth_mac_addr_t *t) -{ - bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_t)); - if (ok){ - src_mac_addr = *t; - src_mac_addr_initialized = true; - //eth_mac_set_addr(t); //this breaks the link - } - - return ok; } -//////////////////// IP Addr Stuff /////////////////////// - -static bool src_ip_addr_initialized = false; - -static const struct ip_addr default_ip_addr = { - (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) -}; - -static struct ip_addr src_ip_addr = { - (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) -}; - -void set_default_ip_addr(void) -{ - src_ip_addr_initialized = true; - src_ip_addr = default_ip_addr; +const eth_mac_addr_t *ethernet_mac_addr(void){ + eth_addrs_init(); + return ¤t_eth_addrs.mac_addr; } -const struct ip_addr *get_ip_addr(void) -{ - if (!src_ip_addr_initialized){ // fetch from eeprom - src_ip_addr_initialized = true; - - // if we're simulating, don't read the EEPROM model, it's REALLY slow - if (hwconfig_simulation_p()) - return &src_ip_addr; +const struct ip_addr *get_ip_addr(void){ + eth_addrs_init(); + return ¤t_eth_addrs.ip_addr; +} - struct ip_addr tmp; - bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, &tmp, sizeof(tmp)); - if (!ok || unprogrammed(&tmp, sizeof(tmp))){ - // use the default - } - else - src_ip_addr = tmp; - } +const struct ip_addr *get_subnet(void){ + eth_addrs_init(); + return ¤t_eth_addrs.subnet; +} - return &src_ip_addr; +const struct ip_addr *get_gateway(void){ + eth_addrs_init(); + return ¤t_eth_addrs.gateway; } bool set_ip_addr(const struct ip_addr *t){ - bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); - if (ok){ - src_ip_addr = *t; - src_ip_addr_initialized = true; - } + const bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); + if (ok) current_eth_addrs.ip_addr = *t; + return ok; +} - return ok; +void eth_addrs_set_default(void){ + eth_addrs_initialized = true; + memcpy(¤t_eth_addrs, &default_eth_addrs, sizeof(default_eth_addrs)); } diff --git a/firmware/zpu/lib/ethernet.h b/firmware/zpu/lib/ethernet.h index 52b297349..b5b08cb8c 100644 --- a/firmware/zpu/lib/ethernet.h +++ b/firmware/zpu/lib/ethernet.h @@ -1,5 +1,5 @@ -/* -*- c -*- */ /* + * Copyright 2010-2012 Ettus Research LLC * Copyright 2007 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify @@ -44,27 +44,28 @@ void ethernet_register_link_changed_callback(ethernet_link_changed_callback_t cb */ const eth_mac_addr_t *ethernet_mac_addr(void); -/*!set mac addr to default*/ -void set_default_mac_addr(void); - /*! - * \brief write mac address to eeprom and begin using it + * \returns IP address */ -bool ethernet_set_mac_addr(const eth_mac_addr_t *t); +const struct ip_addr *get_ip_addr(void); /*! - * \returns IP address + * \returns gateway address */ -const struct ip_addr *get_ip_addr(void); +const struct ip_addr *get_gateway(void); -/*!set ip addr to default*/ -void set_default_ip_addr(void); +/*! + * \returns subnet address + */ +const struct ip_addr *get_subnet(void); /*! * \brief write ip address to eeprom and begin using it */ bool set_ip_addr(const struct ip_addr *t); +//! Apply default settings to eth addrs +void eth_addrs_set_default(void); /* * \brief read RMON regs and return error mask diff --git a/firmware/zpu/lib/memory_map.h b/firmware/zpu/lib/memory_map.h index 9d47522ca..4290ee20a 100644 --- a/firmware/zpu/lib/memory_map.h +++ b/firmware/zpu/lib/memory_map.h @@ -1,4 +1,4 @@ -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC /* * Copyright 2007,2008,2009 Free Software Foundation, Inc. * @@ -49,18 +49,6 @@ // SPI Core, Slave 2. See core docs for more info ///////////////////////////////////////////////////// -typedef struct { - volatile uint32_t txrx0; - volatile uint32_t txrx1; - volatile uint32_t txrx2; - volatile uint32_t txrx3; - volatile uint32_t ctrl; - volatile uint32_t div; - volatile uint32_t ss; -} spi_regs_t; - -#define spi_regs ((spi_regs_t *) SPI_BASE) - // Masks for controlling different peripherals #define SPI_SS_AD9510 1 #define SPI_SS_AD9777 2 @@ -124,7 +112,8 @@ typedef struct { /////////////////////////////////////////////////// typedef struct { - volatile uint32_t _padding[8]; + volatile uint32_t spi; + volatile uint32_t _padding[7]; volatile uint32_t status; volatile uint32_t _unused; volatile uint32_t time64_secs_rb; @@ -133,7 +122,10 @@ typedef struct { volatile uint32_t irqs; } router_status_t; +#define SPI_READY_IRQ (1 << 12) + #define router_status ((router_status_t *) READBACK_BASE) +#define readback_mux ((router_status_t *) READBACK_BASE) //alias with a better name /*! * \brief return non-zero if we're running under the simulator @@ -207,7 +199,7 @@ typedef struct { #define SR_SIMTIMER 8 // 2 #define SR_TIME64 10 // 6 #define SR_BUF_POOL 16 // 4 - +#define SR_SPI_CORE 20 // 3 #define SR_RX_FRONT 24 // 5 #define SR_RX_CTRL0 32 // 9 #define SR_RX_DSP0 48 // 7 @@ -224,6 +216,21 @@ typedef struct { #define SR_ADDR_BLDRDONE _SR_ADDR(5) +// --- spi core control regs --- + +typedef struct { + volatile uint32_t divider; + volatile uint32_t control; + volatile uint32_t data; +} spi_core_t; + +#define SPI_CORE_SLAVE_SELECT_SHIFT 0 +#define SPI_CORE_NUM_BITS_SHIFT 24 +#define SPI_CORE_DATA_IN_EDGE_SHIFT 30 +#define SPI_CORE_DATA_OUT_EDGE_SHIFT 31 + +#define spi_core ((spi_core_t *) _SR_ADDR(SR_SPI_CORE)) + // --- packet router control regs --- typedef struct { diff --git a/firmware/zpu/lib/net_common.c b/firmware/zpu/lib/net_common.c index 42e365393..9b75006d3 100644 --- a/firmware/zpu/lib/net_common.c +++ b/firmware/zpu/lib/net_common.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Ettus Research LLC + * Copyright 2009-2012 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 @@ -374,6 +374,22 @@ send_arp_reply(struct arp_eth_ipv4 *req, eth_mac_addr_t our_mac) send_pkt(t, ETHERTYPE_ARP, &reply, sizeof(reply), 0, 0, 0, 0); } +void net_common_send_arp_request(const struct ip_addr *addr){ + struct arp_eth_ipv4 req _AL4; + req.ar_hrd = ARPHRD_ETHER; + req.ar_pro = ETHERTYPE_IPV4; + req.ar_hln = sizeof(eth_mac_addr_t); + req.ar_pln = sizeof(struct ip_addr); + req.ar_op = ARPOP_REQUEST; + memcpy(req.ar_sha, ethernet_mac_addr(), sizeof(eth_mac_addr_t)); + memcpy(req.ar_sip, get_ip_addr(), sizeof(struct ip_addr)); + memset(req.ar_tha, 0x00, sizeof(eth_mac_addr_t)); + memcpy(req.ar_tip, addr, sizeof(struct ip_addr)); + + //send the request with a broadcast ethernet mac address + send_pkt(BCAST_MAC_ADDR, ETHERTYPE_ARP, &req, sizeof(req), 0, 0, 0, 0); +} + void send_gratuitous_arp(void){ struct arp_eth_ipv4 req _AL4; req.ar_hrd = ARPHRD_ETHER; @@ -415,7 +431,15 @@ handle_arp_packet(struct arp_eth_ipv4 *p, size_t size) || p->ar_hln != 6 || p->ar_pln != 4) return; - + + if (p->ar_op == ARPOP_REPLY){ + struct ip_addr ip_addr; + memcpy(&ip_addr, p->ar_sip, sizeof(ip_addr)); + eth_mac_addr_t mac_addr; + memcpy(&mac_addr, p->ar_sha, sizeof(mac_addr)); + arp_cache_update(&ip_addr, &mac_addr); + } + if (p->ar_op != ARPOP_REQUEST) return; diff --git a/firmware/zpu/lib/net_common.h b/firmware/zpu/lib/net_common.h index 3cbc5c514..5e6daf689 100644 --- a/firmware/zpu/lib/net_common.h +++ b/firmware/zpu/lib/net_common.h @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Ettus Research LLC + * Copyright 2009-2012 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 @@ -55,4 +55,7 @@ void handle_eth_packet(uint32_t *p, size_t nlines); void send_gratuitous_arp(void); +//! Send an ARP request for the given IP address +void net_common_send_arp_request(const struct ip_addr *addr); + #endif /* INCLUDED_NET_COMMON_H */ diff --git a/firmware/zpu/lib/spi.c b/firmware/zpu/lib/spi.c index af0d8a68f..6f2f74899 100644 --- a/firmware/zpu/lib/spi.c +++ b/firmware/zpu/lib/spi.c @@ -1,4 +1,5 @@ /* + * Copyright 2012 Ettus Research LLC * Copyright 2007,2008 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify @@ -17,94 +18,38 @@ #include "spi.h" #include "memory_map.h" -#include "pic.h" #include "nonstdio.h" -//void (*volatile spi_callback)(void); //SPI callback when xfer complete. - -//static void spi_irq_handler(unsigned irq); - -void -spi_init(void) +void spi_init(void) { - /* - * f_sclk = f_wb / ((div + 1) * 2) - */ - spi_regs->div = 1; // 0 = Div by 2 (25 MHz); 1 = Div-by-4 (12.5 MHz) + spi_core->divider = 10; } -void -spi_wait(void) +void spi_wait(void) { - while (spi_regs->ctrl & SPI_CTRL_GO_BSY) - ; + while ((readback_mux->irqs & SPI_READY_IRQ) == 0){ + //NOP + } } -uint32_t -spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags) +uint32_t spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags) { - flags &= (SPI_CTRL_TXNEG | SPI_CTRL_RXNEG); - int ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & length) | flags; - - spi_wait(); - - // Tell it which SPI slave device to access - spi_regs->ss = slave & 0xffff; - - // Data we will send - spi_regs->txrx0 = data; + uint32_t control_word = 0; + control_word |= (slave << SPI_CORE_SLAVE_SELECT_SHIFT); + control_word |= (length << SPI_CORE_NUM_BITS_SHIFT); + if ((flags & SPI_PUSH_RISE) != 0) control_word |= (1 << SPI_CORE_DATA_OUT_EDGE_SHIFT); + if ((flags & SPI_PUSH_FALL) != 0) control_word |= (0 << SPI_CORE_DATA_OUT_EDGE_SHIFT); + if ((flags & SPI_LATCH_RISE) != 0) control_word |= (1 << SPI_CORE_DATA_IN_EDGE_SHIFT); + if ((flags & SPI_LATCH_FALL) != 0) control_word |= (0 << SPI_CORE_DATA_IN_EDGE_SHIFT); - // Run it -- write once and rewrite with GO set - spi_regs->ctrl = ctrl; - spi_regs->ctrl = ctrl | SPI_CTRL_GO_BSY; + const uint32_t data_out = data << (32 - length); - if(readback) { spi_wait(); - return spi_regs->txrx0; - } - else - return 0; -} + spi_core->control = control_word; + spi_core->data = data_out; -/* -void spi_register_callback(void (*volatile callback)(void)) { - spi_callback = callback; -} - -static void spi_irq_handler(unsigned irq) { -// printf("SPI IRQ handler\n"); -// uint32_t wat = spi_regs->ctrl; //read a register just to clear the interrupt - //spi_regs->ctrl &= ~SPI_CTRL_IE; - if(spi_callback) spi_callback(); //we could just use the PIC to register the user's callback, but this provides the ability to do other things later -} + if (!readback) return 0; -uint32_t spi_get_data(void) { - return spi_regs->txrx0; -} - -bool -spi_async_transact(int slave, uint32_t data, int length, uint32_t flags, void (*volatile callback)(void)) { - flags &= (SPI_CTRL_TXNEG | SPI_CTRL_RXNEG); - int ctrl = SPI_CTRL_ASS | SPI_CTRL_IE | (SPI_CTRL_CHAR_LEN_MASK & length) | flags; - - if(spi_regs->ctrl & SPI_CTRL_GO_BSY) { - printf("Async SPI busy!\n"); - return false; //we don't wait on busy, we just return failure. we count on the host to not set up another transaction before the last one finishes. - } - - // Tell it which SPI slave device to access - spi_regs->ss = slave & 0xffff; - - // Data we will send - spi_regs->txrx0 = data; - - spi_register_callback(callback); - pic_register_handler(IRQ_SPI, spi_irq_handler); - - // Run it -- write once and rewrite with GO set - spi_regs->ctrl = ctrl; - spi_regs->ctrl = ctrl | SPI_CTRL_GO_BSY; - - return true; + spi_wait(); + return readback_mux->spi; } -*/ diff --git a/firmware/zpu/lib/spi.h b/firmware/zpu/lib/spi.h index 71245150a..125e1a502 100644 --- a/firmware/zpu/lib/spi.h +++ b/firmware/zpu/lib/spi.h @@ -1,5 +1,5 @@ -/* -*- c -*- */ /* + * Copyright 2012 Ettus Research LLC * Copyright 2006,2007 Free Software Foundation, Inc. * * This program is free software: you can redistribute it and/or modify @@ -19,8 +19,8 @@ #ifndef INCLUDED_SPI_H #define INCLUDED_SPI_H -#include <memory_map.h> #include <stdbool.h> +#include <stdint.h> /*! * \brief One time call to initialize SPI @@ -39,39 +39,11 @@ void spi_wait(void); /* * Flags for spi_transact */ -#define SPIF_PUSH_RISE 0 // push tx data on rising edge of SCLK -#define SPIF_PUSH_FALL SPI_CTRL_TXNEG // push tx data on falling edge of SCLK -#define SPIF_LATCH_RISE 0 // latch rx data on rising edge of SCLK -#define SPIF_LATCH_FALL SPI_CTRL_RXNEG // latch rx data on falling edge of SCLK - - -uint32_t -spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags); - -//uint32_t spi_get_data(void); -//static void spi_irq_handler(unsigned irq); -//void spi_register_callback(void (*volatile callback)(void)); - -//bool -//spi_async_transact(int slave, uint32_t data, int length, uint32_t flags, void (*volatile callback)(void)); - -// ---------------------------------------------------------------- -// Routines that manipulate the FLASH SPI BUS -// ---------------------------------------------------------------- - -/*! - * \brief One time call to initialize SPI - */ -void spif_init(void); - -/*! - * \brief Wait for last SPI transaction to complete. - * Unless you need to know it completed, it's not necessary to call this. - */ -void spif_wait(void); - -uint32_t -spif_transact(bool readback_, int slave, uint32_t data, int length, uint32_t flags); +#define SPI_PUSH_RISE (1 << 0) // push tx data on rising edge of SCLK +#define SPI_PUSH_FALL (1 << 1) // push tx data on falling edge of SCLK +#define SPI_LATCH_RISE (1 << 2) // latch rx data on rising edge of SCLK +#define SPI_LATCH_FALL (1 << 3) // latch rx data on falling edge of SCLK +uint32_t spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags); #endif /* INCLUDED_SPI_H */ diff --git a/firmware/zpu/lib/u2_init.c b/firmware/zpu/lib/u2_init.c index 71bd2c594..77c8c0722 100644 --- a/firmware/zpu/lib/u2_init.c +++ b/firmware/zpu/lib/u2_init.c @@ -51,6 +51,7 @@ u2_init(void) hal_enable_ints(); // flash all leds to let us know board is alive +#ifndef BOOTLOADER hal_set_led_src(0x0, 0x1f); /* software ctrl */ hal_set_leds(0x0, 0x1f); mdelay(300); hal_set_leds(LED_E, LED_E); mdelay(300); @@ -61,6 +62,7 @@ u2_init(void) hal_set_leds(0x0, 0x1f); mdelay(100); hal_set_leds(blinks, 0x1f); mdelay(100); } +#endif hal_set_led_src(0x1f & ~LED_D, 0x1f); /* hardware ctrl */ hal_set_leds(LED_D, 0x1f); // Leave one on diff --git a/firmware/zpu/usrp2p/spi_flash.c b/firmware/zpu/usrp2p/spi_flash.c index 9406f8042..09f908edb 100644 --- a/firmware/zpu/usrp2p/spi_flash.c +++ b/firmware/zpu/usrp2p/spi_flash.c @@ -1,7 +1,6 @@ -/* -*- c++ -*- */ /* * Copyright 2009 Free Software Foundation, Inc. - * Copyright 2009-2011 Ettus Research LLC + * Copyright 2009-2012 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 @@ -17,6 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "spi_flash.h" #include "spi_flash_private.h" //#include <stdlib.h> #include <nonstdio.h> diff --git a/firmware/zpu/usrp2p/spi_flash.h b/firmware/zpu/usrp2p/spi_flash.h index a10533e08..8a8facdca 100644 --- a/firmware/zpu/usrp2p/spi_flash.h +++ b/firmware/zpu/usrp2p/spi_flash.h @@ -23,10 +23,18 @@ #include <stdint.h> #include <stdbool.h> - #define SPI_FLASH_PAGE_SIZE 256 #define SPI_SS_FLASH 1 +#define SPIF_PUSH_RISE 0 // push tx data on rising edge of SCLK +#define SPIF_PUSH_FALL SPI_CTRL_TXNEG // push tx data on falling edge of SCLK +#define SPIF_LATCH_RISE 0 // latch rx data on rising edge of SCLK +#define SPIF_LATCH_FALL SPI_CTRL_RXNEG // latch rx data on falling edge of SCLK + +void spif_init(void); +void spif_wait(void); + +uint32_t spif_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags); uint32_t spi_flash_rdid(void); /* Read ID */ uint32_t spi_flash_rdsr(void); /* Read Status Register */ diff --git a/firmware/zpu/usrp2p/spif.c b/firmware/zpu/usrp2p/spif.c index 91da73155..60807ca4a 100644 --- a/firmware/zpu/usrp2p/spif.c +++ b/firmware/zpu/usrp2p/spif.c @@ -21,6 +21,7 @@ */ #include "spi.h" +#include "spi_flash.h" #include "memory_map.h" void diff --git a/firmware/zpu/usrp2p/u2p_init.c b/firmware/zpu/usrp2p/u2p_init.c index 381987ae6..1890dd726 100644 --- a/firmware/zpu/usrp2p/u2p_init.c +++ b/firmware/zpu/usrp2p/u2p_init.c @@ -24,7 +24,6 @@ void u2p_init(void){ bool safe_fw = find_safe_booted_flag(); set_safe_booted_flag(0); if (safe_fw) { - set_default_ip_addr(); - set_default_mac_addr(); + eth_addrs_set_default(); } } diff --git a/firmware/zpu/usrp2p/udp_fw_update.c b/firmware/zpu/usrp2p/udp_fw_update.c index 5689388a8..cd9e7d902 100644 --- a/firmware/zpu/usrp2p/udp_fw_update.c +++ b/firmware/zpu/usrp2p/udp_fw_update.c @@ -1,6 +1,5 @@ -/* -*- c++ -*- */ /* - * Copyright 2010 Ettus Research LLC + * Copyright 2010-2012 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 @@ -19,6 +18,7 @@ //Routines to handle updating the SPI Flash firmware via UDP #include <net_common.h> +#include "memory_map.h" #include "usrp2/fw_common.h" #include "spi.h" #include "spi_flash.h" diff --git a/fpga/usrp2/control_lib/Makefile.srcs b/fpga/usrp2/control_lib/Makefile.srcs index 6ee7ea262..42862a50f 100644 --- a/fpga/usrp2/control_lib/Makefile.srcs +++ b/fpga/usrp2/control_lib/Makefile.srcs @@ -55,4 +55,7 @@ atr_controller16.v \ fifo_to_wb.v \ gpio_atr.v \ user_settings.v \ +settings_fifo_ctrl.v \ +simple_spi_core.v \ +simple_i2c_core.v \ )) diff --git a/fpga/usrp2/control_lib/settings_bus_crossclock.v b/fpga/usrp2/control_lib/settings_bus_crossclock.v index 9c5912042..a61ee8fad 100644 --- a/fpga/usrp2/control_lib/settings_bus_crossclock.v +++ b/fpga/usrp2/control_lib/settings_bus_crossclock.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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,16 +22,17 @@ // the system or dsp clock on the output side module settings_bus_crossclock + #(parameter FLOW_CTRL=0) (input clk_i, input rst_i, input set_stb_i, input [7:0] set_addr_i, input [31:0] set_data_i, - input clk_o, input rst_o, output set_stb_o, output [7:0] set_addr_o, output [31:0] set_data_o); + input clk_o, input rst_o, output set_stb_o, output [7:0] set_addr_o, output [31:0] set_data_o, input blocked); wire full, empty; fifo_xlnx_16x40_2clk settings_fifo (.rst(rst_i), .wr_clk(clk_i), .din({set_addr_i,set_data_i}), .wr_en(set_stb_i & ~full), .full(full), - .rd_clk(clk_o), .dout({set_addr_o,set_data_o}), .rd_en(~empty), .empty(empty)); + .rd_clk(clk_o), .dout({set_addr_o,set_data_o}), .rd_en(set_stb_o), .empty(empty)); - assign set_stb_o = ~empty; + assign set_stb_o = ~empty & (~blocked | ~FLOW_CTRL); endmodule // settings_bus_crossclock diff --git a/fpga/usrp2/control_lib/settings_fifo_ctrl.v b/fpga/usrp2/control_lib/settings_fifo_ctrl.v new file mode 100644 index 000000000..37f11776e --- /dev/null +++ b/fpga/usrp2/control_lib/settings_fifo_ctrl.v @@ -0,0 +1,395 @@ +// +// Copyright 2012 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/>. +// + +// A settings and readback bus controlled via fifo36 interface + +module settings_fifo_ctrl + #( + parameter XPORT_HDR = 1, //extra transport hdr line + parameter PROT_DEST = 0, //protocol framer destination + parameter PROT_HDR = 1, //needs a protocol header? + parameter ACK_SID = 0 //stream ID for packet ACK + ) + ( + //clock and synchronous reset for all interfaces + input clock, input reset, input clear, + + //current system time + input [63:0] vita_time, + + //ready signal for multiple peripherals + input perfs_ready, + + //input fifo36 interface control + input [35:0] in_data, input in_valid, output in_ready, + + //output fifo36 interface status + output [35:0] out_data, output out_valid, input out_ready, + + //32-bit settings bus outputs + output strobe, output [7:0] addr, output [31:0] data, + + //16X 32-bit inputs for readback + input [31:0] word00, + input [31:0] word01, + input [31:0] word02, + input [31:0] word03, + input [31:0] word04, + input [31:0] word05, + input [31:0] word06, + input [31:0] word07, + input [31:0] word08, + input [31:0] word09, + input [31:0] word10, + input [31:0] word11, + input [31:0] word12, + input [31:0] word13, + input [31:0] word14, + input [31:0] word15, + + //debug output + output [31:0] debug + ); + + wire reading = in_valid && in_ready; + wire writing = out_valid && out_ready; + + //------------------------------------------------------------------ + //-- The command fifo: + //-- Stores an individual register access command per line. + //------------------------------------------------------------------ + wire [63:0] in_command_ticks, out_command_ticks; + wire [31:0] in_command_hdr, out_command_hdr; + wire [31:0] in_command_data, out_command_data; + wire in_command_has_time, out_command_has_time; + wire command_fifo_full, command_fifo_empty; + wire command_fifo_read, command_fifo_write; + + shortfifo #(.WIDTH(129)) command_fifo ( + .clk(clock), .rst(reset), .clear(clear), + .datain({in_command_ticks, in_command_hdr, in_command_data, in_command_has_time}), + .dataout({out_command_ticks, out_command_hdr, out_command_data, out_command_has_time}), + .write(command_fifo_write), .full(command_fifo_full), //input interface + .empty(command_fifo_empty), .read(command_fifo_read) //output interface + ); + + //------------------------------------------------------------------ + //-- The result fifo: + //-- Stores an individual result of a command per line. + //------------------------------------------------------------------ + wire [31:0] in_result_hdr, out_result_hdr; + wire [31:0] in_result_data, out_result_data; + wire result_fifo_full, result_fifo_empty; + wire result_fifo_read, result_fifo_write; + + shortfifo #(.WIDTH(64)) result_fifo ( + .clk(clock), .rst(reset), .clear(clear), + .datain({in_result_hdr, in_result_data}), + .dataout({out_result_hdr, out_result_data}), + .write(result_fifo_write), .full(result_fifo_full), //input interface + .empty(result_fifo_empty), .read(result_fifo_read) //output interface + ); + + //------------------------------------------------------------------ + //-- Input state machine: + //-- Read input packet and fill a command fifo entry. + //------------------------------------------------------------------ + localparam READ_LINE0 = 0; + localparam VITA_HDR = 1; + localparam VITA_SID = 2; + localparam VITA_CID0 = 3; + localparam VITA_CID1 = 4; + localparam VITA_TSI = 5; + localparam VITA_TSF0 = 6; + localparam VITA_TSF1 = 7; + localparam READ_HDR = 8; + localparam READ_DATA = 9; + localparam WAIT_EOF = 10; + localparam STORE_CMD = 11; + + localparam START_STATE = (XPORT_HDR)? READ_LINE0 : VITA_HDR; + + reg [4:0] in_state; + + //holdover from current read inputs + reg [31:0] in_data_reg, in_hdr_reg; + reg [63:0] in_ticks_reg; + wire has_sid = in_data[28]; + wire has_cid = in_data[27]; + wire has_tsi = in_data[23:22] != 0; + wire has_tsf = in_data[21:20] != 0; + reg has_sid_reg, has_cid_reg, has_tsi_reg, has_tsf_reg; + + assign in_ready = (in_state < STORE_CMD); + assign command_fifo_write = (in_state == STORE_CMD); + assign in_command_ticks = in_ticks_reg; + assign in_command_data = in_data_reg; + assign in_command_hdr = in_hdr_reg; + assign in_command_has_time = has_tsf_reg; + + always @(posedge clock) begin + if (reset) begin + in_state <= START_STATE; + end + else begin + case (in_state) + + READ_LINE0: begin + if (reading) in_state <= VITA_HDR; + end + + VITA_HDR: begin + if (reading) begin + if (has_sid) in_state <= VITA_SID; + else if (has_cid) in_state <= VITA_CID0; + else if (has_tsi) in_state <= VITA_TSI; + else if (has_tsf) in_state <= VITA_TSF0; + else in_state <= READ_HDR; + end + has_sid_reg <= has_sid; + has_cid_reg <= has_cid; + has_tsi_reg <= has_tsi; + has_tsf_reg <= has_tsf; + end + + VITA_SID: begin + if (reading) begin + if (has_cid_reg) in_state <= VITA_CID0; + else if (has_tsi_reg) in_state <= VITA_TSI; + else if (has_tsf_reg) in_state <= VITA_TSF0; + else in_state <= READ_HDR; + end + end + + VITA_CID0: begin + if (reading) in_state <= VITA_CID1; + end + + VITA_CID1: begin + if (reading) begin + if (has_tsi_reg) in_state <= VITA_TSI; + else if (has_tsf_reg) in_state <= VITA_TSF0; + else in_state <= READ_HDR; + end + end + + VITA_TSI: begin + if (reading) begin + if (has_tsf_reg) in_state <= VITA_TSF0; + else in_state <= READ_HDR; + end + end + + VITA_TSF0: begin + if (reading) in_state <= VITA_TSF1; + in_ticks_reg[63:32] <= in_data; + end + + VITA_TSF1: begin + if (reading) in_state <= READ_HDR; + in_ticks_reg[31:0] <= in_data; + end + + READ_HDR: begin + if (reading) in_state <= READ_DATA; + in_hdr_reg <= in_data[31:0]; + end + + READ_DATA: begin + if (reading) in_state <= (in_data[33])? STORE_CMD : WAIT_EOF; + in_data_reg <= in_data[31:0]; + end + + WAIT_EOF: begin + if (reading && in_data[33]) in_state <= STORE_CMD; + end + + STORE_CMD: begin + if (~command_fifo_full) in_state <= START_STATE; + end + + endcase //in_state + end + end + + //------------------------------------------------------------------ + //-- Command state machine: + //-- Read a command fifo entry, act on it, produce result. + //------------------------------------------------------------------ + localparam LOAD_CMD = 0; + localparam EVENT_CMD = 1; + + reg cmd_state; + reg [31:0] rb_data; + + reg [63:0] command_ticks_reg; + reg [31:0] command_hdr_reg; + reg [31:0] command_data_reg; + + reg [63:0] vita_time_reg; + always @(posedge clock) + vita_time_reg <= vita_time; + + wire late; + `ifndef FIFO_CTRL_NO_TIME + time_compare time_compare( + .time_now(vita_time_reg), .trigger_time(command_ticks_reg), .late(late)); + `else + assign late = 1; + `endif + + //action occurs in the event state and when there is fifo space (should always be true) + //the third condition is that all peripherals in the perfs signal are ready/active high + //the fourth condition is that is an event time has been set, action is delayed until that time + wire time_ready = (out_command_has_time)? late : 1; + wire action = (cmd_state == EVENT_CMD) && ~result_fifo_full && perfs_ready && time_ready; + + assign command_fifo_read = action; + assign result_fifo_write = action; + assign in_result_hdr = command_hdr_reg; + assign in_result_data = rb_data; + + always @(posedge clock) begin + if (reset) begin + cmd_state <= LOAD_CMD; + end + else begin + case (cmd_state) + + LOAD_CMD: begin + if (~command_fifo_empty) cmd_state <= EVENT_CMD; + command_ticks_reg <= out_command_ticks; + command_hdr_reg <= out_command_hdr; + command_data_reg <= out_command_data; + end + + EVENT_CMD: begin // poking and peeking happens here! + if (action || clear) cmd_state <= LOAD_CMD; + end + + endcase //cmd_state + end + end + + //------------------------------------------------------------------ + //-- assign to settings bus interface + //------------------------------------------------------------------ + reg strobe_reg; + assign strobe = strobe_reg; + assign data = command_data_reg; + assign addr = command_hdr_reg[7:0]; + wire poke = command_hdr_reg[8]; + + always @(posedge clock) begin + if (reset || clear) strobe_reg <= 0; + else strobe_reg <= action && poke; + end + + //------------------------------------------------------------------ + //-- readback mux + //------------------------------------------------------------------ + always @(posedge clock) begin + case (out_command_hdr[3:0]) + 0 : rb_data <= word00; + 1 : rb_data <= word01; + 2 : rb_data <= word02; + 3 : rb_data <= word03; + 4 : rb_data <= word04; + 5 : rb_data <= word05; + 6 : rb_data <= word06; + 7 : rb_data <= word07; + 8 : rb_data <= word08; + 9 : rb_data <= word09; + 10: rb_data <= word10; + 11: rb_data <= word11; + 12: rb_data <= word12; + 13: rb_data <= word13; + 14: rb_data <= word14; + 15: rb_data <= word15; + endcase // case(addr_reg[3:0]) + end + + //------------------------------------------------------------------ + //-- Output state machine: + //-- Read a command fifo entry, act on it, produce ack packet. + //------------------------------------------------------------------ + localparam WRITE_PROT_HDR = 0; + localparam WRITE_VRT_HDR = 1; + localparam WRITE_VRT_SID = 2; + localparam WRITE_RB_HDR = 3; + localparam WRITE_RB_DATA = 4; + + //the state for the start of packet condition + localparam WRITE_PKT_HDR = (PROT_HDR)? WRITE_PROT_HDR : WRITE_VRT_HDR; + + reg [2:0] out_state; + + assign out_valid = ~result_fifo_empty; + assign result_fifo_read = out_data[33] && writing; + + always @(posedge clock) begin + if (reset) begin + out_state <= WRITE_PKT_HDR; + end + else if (writing && out_data[33]) begin + out_state <= WRITE_PKT_HDR; + end + else if (writing) begin + out_state <= out_state + 1; + end + end + + //------------------------------------------------------------------ + //-- assign to output fifo interface + //------------------------------------------------------------------ + wire [31:0] prot_hdr; + assign prot_hdr[15:0] = 16; //bytes in proceeding vita packet + assign prot_hdr[16] = 1; //yes frame + assign prot_hdr[18:17] = PROT_DEST; + assign prot_hdr[31:19] = 0; //nothing + + reg [31:0] out_data_int; + always @* begin + case (out_state) + WRITE_PROT_HDR: out_data_int <= prot_hdr; + WRITE_VRT_HDR: out_data_int <= {12'b010100000000, out_result_hdr[19:16], 2'b0, prot_hdr[15:2]}; + WRITE_VRT_SID: out_data_int <= ACK_SID; + WRITE_RB_HDR: out_data_int <= out_result_hdr; + WRITE_RB_DATA: out_data_int <= out_result_data; + default: out_data_int <= 0; + endcase //state + end + + assign out_data[35:34] = 2'b0; + assign out_data[33] = (out_state == WRITE_RB_DATA); + assign out_data[32] = (out_state == WRITE_PKT_HDR); + assign out_data[31:0] = out_data_int; + + //------------------------------------------------------------------ + //-- debug outputs + //------------------------------------------------------------------ + assign debug = { + in_state, out_state, //8 + in_valid, in_ready, in_data[33:32], //4 + out_valid, out_ready, out_data[33:32], //4 + command_fifo_empty, command_fifo_full, //2 + command_fifo_read, command_fifo_write, //2 + addr, //8 + strobe_reg, strobe, poke, out_command_has_time //4 + }; + +endmodule //settings_fifo_ctrl diff --git a/fpga/usrp2/control_lib/simple_i2c_core.v b/fpga/usrp2/control_lib/simple_i2c_core.v new file mode 100644 index 000000000..9c61de8fb --- /dev/null +++ b/fpga/usrp2/control_lib/simple_i2c_core.v @@ -0,0 +1,116 @@ +// +// Copyright 2012 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/>. +// + +// 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/usrp2/control_lib/simple_spi_core.v b/fpga/usrp2/control_lib/simple_spi_core.v new file mode 100644 index 000000000..3c0ed60b9 --- /dev/null +++ b/fpga/usrp2/control_lib/simple_spi_core.v @@ -0,0 +1,214 @@ +// +// Copyright 2012 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/>. +// + +// 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 + reg [31:0] datain_reg; + wire [31:0] datain_next = {datain_reg[30:0], miso}; + 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 diff --git a/fpga/usrp2/coregen/fifo_xlnx_512x36_2clk_prog_full.gise b/fpga/usrp2/coregen/fifo_xlnx_512x36_2clk_prog_full.gise index 9abec8c3e..660fb2f65 100644 --- a/fpga/usrp2/coregen/fifo_xlnx_512x36_2clk_prog_full.gise +++ b/fpga/usrp2/coregen/fifo_xlnx_512x36_2clk_prog_full.gise @@ -21,9 +21,7 @@ <sourceproject xmlns="http://www.xilinx.com/XMLSchema" xil_pn:fileType="FILE_XISE" xil_pn:name="fifo_xlnx_512x36_2clk_prog_full.xise"/>
- <files xmlns="http://www.xilinx.com/XMLSchema">
- <file xil_pn:fileType="FILE_VEO" xil_pn:name="fifo_xlnx_512x36_2clk_prog_full.veo" xil_pn:origination="imported"/>
- </files>
+ <files xmlns="http://www.xilinx.com/XMLSchema"/>
<transforms xmlns="http://www.xilinx.com/XMLSchema"/>
diff --git a/fpga/usrp2/fifo/Makefile.srcs b/fpga/usrp2/fifo/Makefile.srcs index 28d506571..55ba0be2a 100644 --- a/fpga/usrp2/fifo/Makefile.srcs +++ b/fpga/usrp2/fifo/Makefile.srcs @@ -1,5 +1,5 @@ # -# Copyright 2010 Ettus Research LLC +# Copyright 2010-2012 Ettus Research LLC # ################################################## @@ -32,9 +32,11 @@ splitter36.v \ valve36.v \ fifo_pacer.v \ packet_dispatcher36_x3.v \ +packet_dispatcher36_x4.v \ packet_generator32.v \ packet_generator.v \ packet_verifier32.v \ packet_verifier.v \ fifo19_pad.v \ +packet_padder36.v \ )) diff --git a/fpga/usrp2/fifo/packet_dispatcher36_x4.v b/fpga/usrp2/fifo/packet_dispatcher36_x4.v new file mode 100644 index 000000000..7eedb3e74 --- /dev/null +++ b/fpga/usrp2/fifo/packet_dispatcher36_x4.v @@ -0,0 +1,316 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// +// Packet dispatcher with fifo36 interface and 4 outputs. +// +// The packet dispatcher expects 2-byte padded ethernet frames. +// The frames will be inspected at ethernet, IPv4, UDP, and VRT layers. +// Packets are dispatched into the following streams: +// * tx dsp stream +// * tx control stream +// * to cpu stream +// * to external stream +// * to both cpu and external +// +// The following registers are used for dispatcher control: +// * base + 0 = this ipv4 address (32 bits) +// * base + 1 = udp control port (upper 16 bits), udp dsp port (lower 16 bits) +// + +module packet_dispatcher36_x4 + #( + parameter BASE = 0 + ) + ( + //clocking and reset interface: + input clk, input rst, input clr, + + //setting register interface: + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + //input stream interfaces: + input [35:0] com_inp_data, input com_inp_valid, output com_inp_ready, + + //output stream interfaces: + output [35:0] ext_out_data, output ext_out_valid, input ext_out_ready, + output [35:0] dsp_out_data, output dsp_out_valid, input dsp_out_ready, + output [35:0] ctl_out_data, output ctl_out_valid, input ctl_out_ready, + output [35:0] cpu_out_data, output cpu_out_valid, input cpu_out_ready + ); + + //setting register to program the IP address + wire [31:0] my_ip_addr; + setting_reg #(.my_addr(BASE+0)) sreg_ip_addr( + .clk(clk),.rst(rst), + .strobe(set_stb),.addr(set_addr),.in(set_data), + .out(my_ip_addr),.changed() + ); + + //setting register to program the UDP DSP port + wire [15:0] dsp_udp_port, ctl_udp_port; + setting_reg #(.my_addr(BASE+1), .width(32)) sreg_data_port( + .clk(clk),.rst(rst), + .strobe(set_stb),.addr(set_addr),.in(set_data), + .out({ctl_udp_port, dsp_udp_port}),.changed() + ); + + //////////////////////////////////////////////////////////////////// + // Communication input inspector + // - inspect com input and send it to DSP, EXT, CPU, or BOTH + //////////////////////////////////////////////////////////////////// + localparam PD_STATE_READ_COM_PRE = 0; + localparam PD_STATE_READ_COM = 1; + localparam PD_STATE_WRITE_REGS = 2; + localparam PD_STATE_WRITE_LIVE = 3; + + localparam PD_DEST_DSP = 0; + localparam PD_DEST_EXT = 1; + localparam PD_DEST_CPU = 2; + localparam PD_DEST_BOF = 3; + localparam PD_DEST_CTL = 4; + + localparam PD_MAX_NUM_DREGS = 13; //padded_eth + ip + udp + seq + vrt_hdr + localparam PD_DREGS_DSP_OFFSET = 11; //offset to start dsp at + + //output inspector interfaces + wire [35:0] pd_out_dsp_data; + wire pd_out_dsp_valid; + wire pd_out_dsp_ready; + + wire [35:0] pd_out_ext_data; + wire pd_out_ext_valid; + wire pd_out_ext_ready; + + wire [35:0] pd_out_cpu_data; + wire pd_out_cpu_valid; + wire pd_out_cpu_ready; + + wire [35:0] pd_out_bof_data; + wire pd_out_bof_valid; + wire pd_out_bof_ready; + + wire [35:0] pd_out_ctl_data; + wire pd_out_ctl_valid; + wire pd_out_ctl_ready; + + reg [1:0] pd_state; + reg [2:0] pd_dest; + reg [3:0] pd_dreg_count; //data registers to buffer headers + wire [3:0] pd_dreg_count_next = pd_dreg_count + 1'b1; + wire pd_dreg_counter_done = (pd_dreg_count_next == PD_MAX_NUM_DREGS)? 1'b1 : 1'b0; + reg [35:0] pd_dregs [PD_MAX_NUM_DREGS-1:0]; + + reg is_eth_dst_mac_bcast; + reg is_eth_type_ipv4; + reg is_eth_ipv4_proto_udp; + reg is_eth_ipv4_dst_addr_here; + reg is_eth_udp_dsp_port_here; + reg is_eth_udp_ctl_port_here; + wire is_vrt_size_zero = (com_inp_data[15:0] == 16'h0); //needed on the same cycle, so it cant be registered + + //Inspector output flags special case: + //Inject SOF into flags at first DSP line. + wire [3:0] pd_out_flags = ( + (pd_dreg_count == PD_DREGS_DSP_OFFSET) && + (pd_dest == PD_DEST_DSP) + )? 4'b0001 : pd_dregs[pd_dreg_count][35:32]; + + //The communication inspector ouput data and valid signals: + //Mux between com input and data registers based on the state. + wire [35:0] pd_out_data = (pd_state == PD_STATE_WRITE_REGS)? + {pd_out_flags, pd_dregs[pd_dreg_count][31:0]} : com_inp_data + ; + wire pd_out_valid = + (pd_state == PD_STATE_WRITE_REGS)? 1'b1 : ( + (pd_state == PD_STATE_WRITE_LIVE)? com_inp_valid : ( + 1'b0)); + + //The communication inspector ouput ready signal: + //Mux between the various destination ready signals. + wire pd_out_ready = + (pd_dest == PD_DEST_DSP)? pd_out_dsp_ready : ( + (pd_dest == PD_DEST_EXT)? pd_out_ext_ready : ( + (pd_dest == PD_DEST_CPU)? pd_out_cpu_ready : ( + (pd_dest == PD_DEST_BOF)? pd_out_bof_ready : ( + (pd_dest == PD_DEST_CTL)? pd_out_ctl_ready : ( + 1'b0))))); + + //Always connected output data lines. + assign pd_out_dsp_data = pd_out_data; + assign pd_out_ext_data = pd_out_data; + assign pd_out_cpu_data = pd_out_data; + assign pd_out_bof_data = pd_out_data; + assign pd_out_ctl_data = pd_out_data; + + //Destination output valid signals: + //Comes from inspector valid when destination is selected, and otherwise low. + assign pd_out_dsp_valid = (pd_dest == PD_DEST_DSP)? pd_out_valid : 1'b0; + assign pd_out_ext_valid = (pd_dest == PD_DEST_EXT)? pd_out_valid : 1'b0; + assign pd_out_cpu_valid = (pd_dest == PD_DEST_CPU)? pd_out_valid : 1'b0; + assign pd_out_bof_valid = (pd_dest == PD_DEST_BOF)? pd_out_valid : 1'b0; + assign pd_out_ctl_valid = (pd_dest == PD_DEST_CTL)? pd_out_valid : 1'b0; + + //The communication inspector ouput ready signal: + //Always ready when storing to data registers, + //comes from inspector ready output when live, + //and otherwise low. + assign com_inp_ready = + (pd_state == PD_STATE_READ_COM_PRE) ? 1'b1 : ( + (pd_state == PD_STATE_READ_COM) ? 1'b1 : ( + (pd_state == PD_STATE_WRITE_LIVE) ? pd_out_ready : ( + 1'b0))); + + //inspect the incoming data and mark register booleans + always @(posedge clk) + if (com_inp_ready & com_inp_valid) begin + case(pd_dreg_count) + 0: begin + is_eth_dst_mac_bcast <= (com_inp_data[15:0] == 16'hffff); + end + 1: begin + is_eth_dst_mac_bcast <= is_eth_dst_mac_bcast && (com_inp_data[31:0] == 32'hffffffff); + end + 3: begin + is_eth_type_ipv4 <= (com_inp_data[15:0] == 16'h800); + end + 6: begin + is_eth_ipv4_proto_udp <= (com_inp_data[23:16] == 8'h11); + end + 8: begin + is_eth_ipv4_dst_addr_here <= (com_inp_data[31:0] == my_ip_addr); + end + 9: begin + is_eth_udp_dsp_port_here <= (com_inp_data[15:0] == dsp_udp_port); + is_eth_udp_ctl_port_here <= (com_inp_data[15:0] == ctl_udp_port); + end + endcase //pd_dreg_count + end + + always @(posedge clk) + if(rst | clr) begin + pd_state <= PD_STATE_READ_COM_PRE; + pd_dreg_count <= 0; + end + else begin + case(pd_state) + PD_STATE_READ_COM_PRE: begin + if (com_inp_ready & com_inp_valid & com_inp_data[32]) begin + pd_state <= PD_STATE_READ_COM; + pd_dreg_count <= pd_dreg_count_next; + pd_dregs[pd_dreg_count] <= com_inp_data; + end + end + + PD_STATE_READ_COM: begin + if (com_inp_ready & com_inp_valid) begin + pd_dregs[pd_dreg_count] <= com_inp_data; + if (pd_dreg_counter_done | com_inp_data[33]) begin + pd_state <= PD_STATE_WRITE_REGS; + pd_dreg_count <= 0; + + //---------- begin inspection decision -----------// + //EOF or bcast or not IPv4 or not UDP: + if ( + com_inp_data[33] || is_eth_dst_mac_bcast || + ~is_eth_type_ipv4 || ~is_eth_ipv4_proto_udp + ) begin + pd_dest <= PD_DEST_BOF; + end + + //not my IP address: + else if (~is_eth_ipv4_dst_addr_here) begin + pd_dest <= PD_DEST_EXT; + end + + //UDP control port and VRT: + else if (is_eth_udp_ctl_port_here && ~is_vrt_size_zero) begin + pd_dest <= PD_DEST_CTL; + pd_dreg_count <= PD_DREGS_DSP_OFFSET; + end + + //UDP data port and VRT: + else if (is_eth_udp_dsp_port_here && ~is_vrt_size_zero) begin + pd_dest <= PD_DEST_DSP; + pd_dreg_count <= PD_DREGS_DSP_OFFSET; + end + + //other: + else begin + pd_dest <= PD_DEST_CPU; + end + //---------- end inspection decision -------------// + + end + else begin + pd_dreg_count <= pd_dreg_count_next; + end + end + end + + PD_STATE_WRITE_REGS: begin + if (pd_out_ready & pd_out_valid) begin + if (pd_out_data[33]) begin + pd_state <= PD_STATE_READ_COM_PRE; + pd_dreg_count <= 0; + end + else if (pd_dreg_counter_done) begin + pd_state <= PD_STATE_WRITE_LIVE; + pd_dreg_count <= 0; + end + else begin + pd_dreg_count <= pd_dreg_count_next; + end + end + end + + PD_STATE_WRITE_LIVE: begin + if (pd_out_ready & pd_out_valid & pd_out_data[33]) begin + pd_state <= PD_STATE_READ_COM_PRE; + end + end + + endcase //pd_state + end + + //connect this fast-path signals directly to the DSP out + assign dsp_out_data = pd_out_dsp_data; + assign dsp_out_valid = pd_out_dsp_valid; + assign pd_out_dsp_ready = dsp_out_ready; + + assign ctl_out_data = pd_out_ctl_data; + assign ctl_out_valid = pd_out_ctl_valid; + assign pd_out_ctl_ready = ctl_out_ready; + + //////////////////////////////////////////////////////////////////// + // Splitter and output muxes for the bof packets + // - split the bof packets into two streams + // - mux split packets into cpu out and ext out + //////////////////////////////////////////////////////////////////// + + //dummy signals to join the the splitter and muxes below + wire [35:0] _split_to_ext_data, _split_to_cpu_data; + wire _split_to_ext_valid, _split_to_cpu_valid; + wire _split_to_ext_ready, _split_to_cpu_ready; + + splitter36 bof_out_splitter( + .clk(clk), .rst(rst), .clr(clr), + .inp_data(pd_out_bof_data), .inp_valid(pd_out_bof_valid), .inp_ready(pd_out_bof_ready), + .out0_data(_split_to_ext_data), .out0_valid(_split_to_ext_valid), .out0_ready(_split_to_ext_ready), + .out1_data(_split_to_cpu_data), .out1_valid(_split_to_cpu_valid), .out1_ready(_split_to_cpu_ready) + ); + + fifo36_mux ext_out_mux( + .clk(clk), .reset(rst), .clear(clr), + .data0_i(pd_out_ext_data), .src0_rdy_i(pd_out_ext_valid), .dst0_rdy_o(pd_out_ext_ready), + .data1_i(_split_to_ext_data), .src1_rdy_i(_split_to_ext_valid), .dst1_rdy_o(_split_to_ext_ready), + .data_o(ext_out_data), .src_rdy_o(ext_out_valid), .dst_rdy_i(ext_out_ready) + ); + + fifo36_mux cpu_out_mux( + .clk(clk), .reset(rst), .clear(clr), + .data0_i(pd_out_cpu_data), .src0_rdy_i(pd_out_cpu_valid), .dst0_rdy_o(pd_out_cpu_ready), + .data1_i(_split_to_cpu_data), .src1_rdy_i(_split_to_cpu_valid), .dst1_rdy_o(_split_to_cpu_ready), + .data_o(cpu_out_data), .src_rdy_o(cpu_out_valid), .dst_rdy_i(cpu_out_ready) + ); + +endmodule // packet_dispatcher36_x3 diff --git a/fpga/usrp2/fifo/packet_padder36.v b/fpga/usrp2/fifo/packet_padder36.v new file mode 100644 index 000000000..7197b5ea5 --- /dev/null +++ b/fpga/usrp2/fifo/packet_padder36.v @@ -0,0 +1,155 @@ +// +// Copyright 2011-2012 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/>. +// + +// The packet padder 36 for use with RX VITA stream output. +// Packet padder understands the concept of USB LUTs, +// and will forward packets through the interface, +// adding zero padding as needed to properly flush. +// The padder will never write a packet across a LUT boundary. +// When flushing, padder writes out zeros until the LUT boundary. +// Requires that the input line0 be a VITA header, and SOF set. +// Flush when the LUT is partially filled and timeout is reached, +// or when the LUT is partially filled and the DSP is inactive. + +module packet_padder36 +#( + parameter BASE = 0, + + //default is 16K LUT + parameter DEFAULT_LINES32 = 4096, + + //default about 1ms at 64MHz clock + parameter DEFAULT_IDLE_CYC = 65536 +) +( + input clk, input reset, + + //setting bus + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + //input interface + input [35:0] data_i, + input src_rdy_i, + output dst_rdy_o, + + //output interface + output [35:0] data_o, + output src_rdy_o, + input dst_rdy_i, + + input always_flush +); + + wire lut_lines_changed; + wire [15:0] max_lut_lines32; + setting_reg #(.my_addr(BASE+0),.width(16),.at_reset(DEFAULT_LINES32)) sr_num_lines( + .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(max_lut_lines32),.changed(lut_lines_changed)); + + wire idle_cyc_changed; + wire [17:0] idle_flush_cycles; + setting_reg #(.my_addr(BASE+1),.width(18),.at_reset(DEFAULT_IDLE_CYC)) sr_flush_cyc( + .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(idle_flush_cycles),.changed(idle_cyc_changed)); + + //state machine definitions + localparam STATE_READ_HDR = 0; + localparam STATE_WRITE_HDR = 1; + localparam STATE_FORWARD = 2; + localparam STATE_WRITE_PAD = 3; + reg [1:0] state; + + //keep track of the outgoing lines + reg [15:0] line_count; + wire line_count_done = line_count == 1; + wire lut_is_empty = line_count == max_lut_lines32; + always @(posedge clk) begin + if (reset || lut_lines_changed) begin + line_count <= max_lut_lines32; + end + else if (src_rdy_o && dst_rdy_i) begin + line_count <= (line_count_done)? max_lut_lines32 : line_count - 1; + end + end + + //count the number of cycles since RX data so we can force a flush + reg [17:0] non_rx_cycles; + wire idle_timeout = (non_rx_cycles == idle_flush_cycles); + always @(posedge clk) begin + if(reset || state != STATE_READ_HDR || idle_cyc_changed) begin + non_rx_cycles <= 0; + end + else if (~idle_timeout) begin + non_rx_cycles <= non_rx_cycles + 1; + end + end + + //flush when we have written data to a LUT and either idle or non active DSP + wire force_flush = ~lut_is_empty && (idle_timeout || always_flush); + + //the padding state machine + reg [31:0] vita_hdr; + reg has_vita_hdr; + always @(posedge clk) begin + if (reset) begin + state <= STATE_READ_HDR; + end + else case(state) + + STATE_READ_HDR: begin + if (src_rdy_i && dst_rdy_o && data_i[32]) begin + vita_hdr <= data_i[31:0]; + has_vita_hdr <= 1; + state <= (data_i[15:0] > line_count)? state <= STATE_WRITE_PAD : STATE_WRITE_HDR; + end + else if (force_flush) begin + has_vita_hdr <= 0; + state <= STATE_WRITE_PAD; + end + end + + STATE_WRITE_HDR: begin + if (src_rdy_o && dst_rdy_i) begin + state <= STATE_FORWARD; + end + end + + STATE_FORWARD: begin + if (src_rdy_i && dst_rdy_o && data_i[33]) begin + state <= STATE_READ_HDR; + end + end + + STATE_WRITE_PAD: begin + if (src_rdy_o && dst_rdy_i && line_count_done) begin + state <= (has_vita_hdr)? STATE_WRITE_HDR : STATE_READ_HDR; + end + end + + endcase //state + end + + //assign outgoing signals + assign dst_rdy_o = (state == STATE_READ_HDR)? 1 : ((state == STATE_FORWARD)? dst_rdy_i : 0); + assign src_rdy_o = (state == STATE_WRITE_HDR || state == STATE_WRITE_PAD)? 1 : ((state == STATE_FORWARD )? src_rdy_i : 0); + assign data_o = (state == STATE_WRITE_HDR)? {4'b0001, vita_hdr} : ((state == STATE_FORWARD)? data_i : 0); + +endmodule // packet_padder36 + + + + diff --git a/fpga/usrp2/fifo/packet_router.v b/fpga/usrp2/fifo/packet_router.v index 7bfa6893d..4c0fe14b1 100644 --- a/fpga/usrp2/fifo/packet_router.v +++ b/fpga/usrp2/fifo/packet_router.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -54,10 +54,12 @@ module packet_router input [35:0] dsp1_inp_data, input dsp1_inp_valid, output dsp1_inp_ready, input [35:0] eth_inp_data, input eth_inp_valid, output eth_inp_ready, input [35:0] err_inp_data, input err_inp_valid, output err_inp_ready, + input [35:0] ctl_inp_data, input ctl_inp_valid, output ctl_inp_ready, // Output Interfaces (out of router) output [35:0] ser_out_data, output ser_out_valid, input ser_out_ready, output [35:0] dsp_out_data, output dsp_out_valid, input dsp_out_ready, + output [35:0] ctl_out_data, output ctl_out_valid, input ctl_out_ready, output [35:0] eth_out_data, output eth_out_valid, input eth_out_ready ); @@ -188,9 +190,9 @@ module packet_router //////////////////////////////////////////////////////////////////// //dummy signals to join the the muxes below - wire [35:0] _combiner0_data, _combiner1_data; - wire _combiner0_valid, _combiner1_valid; - wire _combiner0_ready, _combiner1_ready; + wire [35:0] _combiner0_data, _combiner1_data, _combiner2_data; + wire _combiner0_valid, _combiner1_valid, _combiner2_valid; + wire _combiner0_ready, _combiner1_ready, _combiner2_ready; fifo36_mux #(.prio(0)) // No priority, fair sharing _com_output_combiner0( @@ -201,6 +203,14 @@ module packet_router ); fifo36_mux #(.prio(0)) // No priority, fair sharing + _com_output_combiner2( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .data0_i(_combiner0_data), .src0_rdy_i(_combiner0_valid), .dst0_rdy_o(_combiner0_ready), + .data1_i(ctl_inp_data), .src1_rdy_i(ctl_inp_valid), .dst1_rdy_o(ctl_inp_ready), + .data_o(_combiner2_data), .src_rdy_o(_combiner2_valid), .dst_rdy_i(_combiner2_ready) + ); + + fifo36_mux #(.prio(0)) // No priority, fair sharing _com_output_combiner1( .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), .data0_i(dsp0_inp_data), .src0_rdy_i(dsp0_inp_valid), .dst0_rdy_o(dsp0_inp_ready), @@ -211,7 +221,7 @@ module packet_router fifo36_mux #(.prio(1)) // Give priority to err/cpu over dsp com_output_source( .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), - .data0_i(_combiner0_data), .src0_rdy_i(_combiner0_valid), .dst0_rdy_o(_combiner0_ready), + .data0_i(_combiner2_data), .src0_rdy_i(_combiner2_valid), .dst0_rdy_o(_combiner2_ready), .data1_i(_combiner1_data), .src1_rdy_i(_combiner1_valid), .dst1_rdy_o(_combiner1_ready), .data_o(udp_out_data), .src_rdy_o(udp_out_valid), .dst_rdy_i(udp_out_ready) ); @@ -248,12 +258,13 @@ module packet_router wire _cpu_out_valid; wire _cpu_out_ready; - packet_dispatcher36_x3 #(.BASE(CTRL_BASE+1)) packet_dispatcher( + packet_dispatcher36_x4 #(.BASE(CTRL_BASE+1)) packet_dispatcher( .clk(stream_clk), .rst(stream_rst), .clr(stream_clr), .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), .com_inp_data(com_inp_data), .com_inp_valid(com_inp_valid), .com_inp_ready(com_inp_ready), .ext_out_data(ext_out_data), .ext_out_valid(ext_out_valid), .ext_out_ready(ext_out_ready), .dsp_out_data(dsp_out_data), .dsp_out_valid(dsp_out_valid), .dsp_out_ready(dsp_out_ready), + .ctl_out_data(ctl_out_data), .ctl_out_valid(ctl_out_valid), .ctl_out_ready(ctl_out_ready), .cpu_out_data(_cpu_out_data), .cpu_out_valid(_cpu_out_valid), .cpu_out_ready(_cpu_out_ready) ); diff --git a/fpga/usrp2/gpif/Makefile.srcs b/fpga/usrp2/gpif/Makefile.srcs index 06cde8afa..7909fb5ff 100644 --- a/fpga/usrp2/gpif/Makefile.srcs +++ b/fpga/usrp2/gpif/Makefile.srcs @@ -1,15 +1,13 @@ # -# Copyright 2010 Ettus Research LLC +# Copyright 2010-2012 Ettus Research LLC # ################################################## # SERDES Sources ################################################## GPIF_SRCS = $(abspath $(addprefix $(BASE_DIR)/../gpif/, \ -gpif.v \ -gpif_wr.v \ -gpif_rd.v \ packet_reframer.v \ -packet_splitter.v \ slave_fifo.v \ +fifo36_to_gpmc16.v \ +gpmc16_to_fifo36.v \ )) diff --git a/fpga/usrp2/gpif/fifo36_to_gpmc16.v b/fpga/usrp2/gpif/fifo36_to_gpmc16.v new file mode 100644 index 000000000..508cd319c --- /dev/null +++ b/fpga/usrp2/gpif/fifo36_to_gpmc16.v @@ -0,0 +1,54 @@ +// +// Copyright 2012 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/>. +// + +module fifo36_to_gpmc16 +#( + parameter FIFO_SIZE = 9 +) +( + //input fifo interface + input fifo_clk, input fifo_rst, + input [35:0] in_data, + input in_src_rdy, + output in_dst_rdy, + + //output interface + input gpif_clk, input gpif_rst, + output [15:0] out_data, + output valid, + input enable, + output eof +); + + wire [35:0] data_int; + wire src_rdy_int, dst_rdy_int; + + fifo_2clock_cascade #(.WIDTH(36), .SIZE(FIFO_SIZE)) fifo_2clk + (.wclk(fifo_clk), .datain(in_data), .src_rdy_i(in_src_rdy), .dst_rdy_o(in_dst_rdy), .space(), + .rclk(gpif_clk), .dataout(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int), .occupied(), + .arst(fifo_rst | gpif_rst)); + + wire [18:0] data18_int; + fifo36_to_fifo19 #(.LE(1)) f36_to_f19 + (.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0), + .f36_datain(data_int), .f36_src_rdy_i(src_rdy_int), .f36_dst_rdy_o(dst_rdy_int), + .f19_dataout(data18_int), .f19_src_rdy_o(valid), .f19_dst_rdy_i(enable) ); + + assign out_data = data18_int[15:0]; + assign eof = data18_int[17]; + +endmodule //fifo_to_gpmc16 diff --git a/fpga/usrp2/gpif/gpif.v b/fpga/usrp2/gpif/gpif.v deleted file mode 100644 index e5b63d5a3..000000000 --- a/fpga/usrp2/gpif/gpif.v +++ /dev/null @@ -1,185 +0,0 @@ -// -// 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/>. -// - -////////////////////////////////////////////////////////////////////////////////// - -module gpif - #(parameter TXFIFOSIZE = 11, parameter RXFIFOSIZE = 11) - (// GPIF signals - input gpif_clk, input gpif_rst, - inout [15:0] gpif_d, input [3:0] gpif_ctl, output [3:0] gpif_rdy, - output [2:0] gpif_misc, - - // Wishbone signals - input wb_clk, input wb_rst, - output [15:0] wb_adr_o, output [15:0] wb_dat_mosi, input [15:0] wb_dat_miso, - output [1:0] wb_sel_o, output wb_cyc_o, output wb_stb_o, output wb_we_o, input wb_ack_i, - input [7:0] triggers, - - // FIFO interface - input fifo_clk, input fifo_rst, input clear_tx, input clear_rx, - output [35:0] tx_data_o, output tx_src_rdy_o, input tx_dst_rdy_i, - input [35:0] rx_data_i, input rx_src_rdy_i, output rx_dst_rdy_o, - input [35:0] tx_err_data_i, input tx_err_src_rdy_i, output tx_err_dst_rdy_o, - - output tx_underrun, output rx_overrun, - input [7:0] frames_per_packet, - output [31:0] debug0, output [31:0] debug1 - ); - - assign tx_underrun = 0; - assign rx_overrun = 0; - - wire WR = gpif_ctl[0]; - wire RD = gpif_ctl[1]; - wire OE = gpif_ctl[2]; - wire EP = gpif_ctl[3]; - - wire CF, CE, DF, DE; - - assign gpif_rdy = { CF, CE, DF, DE }; - - wire [15:0] gpif_d_out; - assign gpif_d = OE ? gpif_d_out : 16'bz; - - wire [15:0] gpif_d_copy = gpif_d; - - wire [31:0] debug_rd, debug_wr, debug_split0, debug_split1; - - // //////////////////////////////////////////////////////////////////// - // TX Data Path - - wire [18:0] tx19_data; - wire tx19_src_rdy, tx19_dst_rdy; - wire [35:0] tx36_data; - wire tx36_src_rdy, tx36_dst_rdy; - - wire [18:0] ctrl_data; - wire ctrl_src_rdy, ctrl_dst_rdy; - - gpif_wr gpif_wr - (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), - .gpif_data(gpif_d), .gpif_wr(WR), .gpif_ep(EP), - .gpif_full_d(DF), .gpif_full_c(CF), - - .sys_clk(fifo_clk), .sys_rst(fifo_rst), - .data_o(tx19_data), .src_rdy_o(tx19_src_rdy), .dst_rdy_i(tx19_dst_rdy), - .ctrl_o(ctrl_data), .ctrl_src_rdy_o(ctrl_src_rdy), .ctrl_dst_rdy_i(ctrl_dst_rdy), - .debug(debug_wr) ); - - // join vita packets which are longer than one frame, drop frame padding - wire [18:0] refr_data; - wire refr_src_rdy, refr_dst_rdy; - - packet_reframer tx_packet_reframer - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .data_i(tx19_data), .src_rdy_i(tx19_src_rdy), .dst_rdy_o(tx19_dst_rdy), - .data_o(refr_data), .src_rdy_o(refr_src_rdy), .dst_rdy_i(refr_dst_rdy)); - - fifo19_to_fifo36 #(.LE(1)) f19_to_f36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(0), - .f19_datain(refr_data), .f19_src_rdy_i(refr_src_rdy), .f19_dst_rdy_o(refr_dst_rdy), - .f36_dataout(tx36_data), .f36_src_rdy_o(tx36_src_rdy), .f36_dst_rdy_i(tx36_dst_rdy)); - - fifo_cascade #(.WIDTH(36), .SIZE(TXFIFOSIZE)) tx_fifo36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .datain(tx36_data), .src_rdy_i(tx36_src_rdy), .dst_rdy_o(tx36_dst_rdy), - .dataout(tx_data_o), .src_rdy_o(tx_src_rdy_o), .dst_rdy_i(tx_dst_rdy_i)); - - // //////////////////////////////////////////// - // RX Data Path - - wire [35:0] rx36_data; - wire rx36_src_rdy, rx36_dst_rdy; - wire [18:0] rx19_data, splt_data; - wire rx19_src_rdy, rx19_dst_rdy, splt_src_rdy, splt_dst_rdy; - wire [18:0] resp_data, resp_int1, resp_int2; - wire resp_src_rdy, resp_dst_rdy; - wire resp_src_rdy_int1, resp_dst_rdy_int1, resp_src_rdy_int2, resp_dst_rdy_int2; - - fifo_cascade #(.WIDTH(36), .SIZE(RXFIFOSIZE)) rx_fifo36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .datain(rx_data_i), .src_rdy_i(rx_src_rdy_i), .dst_rdy_o(rx_dst_rdy_o), - .dataout(rx36_data), .src_rdy_o(rx36_src_rdy), .dst_rdy_i(rx36_dst_rdy)); - - fifo36_to_fifo19 #(.LE(1)) f36_to_f19 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .f36_datain(rx36_data), .f36_src_rdy_i(rx36_src_rdy), .f36_dst_rdy_o(rx36_dst_rdy), - .f19_dataout(rx19_data), .f19_src_rdy_o(rx19_src_rdy), .f19_dst_rdy_i(rx19_dst_rdy) ); - - packet_splitter #(.FRAME_LEN(256)) packet_splitter - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .frames_per_packet(frames_per_packet), - .data_i(rx19_data), .src_rdy_i(rx19_src_rdy), .dst_rdy_o(rx19_dst_rdy), - .data_o(splt_data), .src_rdy_o(splt_src_rdy), .dst_rdy_i(splt_dst_rdy), - .debug0(debug_split0), .debug1(debug_split1)); - - gpif_rd gpif_rd - (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), - .gpif_data(gpif_d_out), .gpif_rd(RD), .gpif_ep(EP), - .gpif_empty_d(DE), .gpif_empty_c(CE), .gpif_flush(gpif_misc[0]), - - .sys_clk(fifo_clk), .sys_rst(fifo_rst), - .data_i(splt_data), .src_rdy_i(splt_src_rdy), .dst_rdy_o(splt_dst_rdy), - .resp_i(resp_data), .resp_src_rdy_i(resp_src_rdy), .resp_dst_rdy_o(resp_dst_rdy), - .debug(debug_rd) ); - - // //////////////////////////////////////////////////////////////////// - // FIFO to Wishbone interface - - fifo_to_wb fifo_to_wb - (.clk(fifo_clk), .reset(fifo_rst), .clear(0), - .data_i(ctrl_data), .src_rdy_i(ctrl_src_rdy), .dst_rdy_o(ctrl_dst_rdy), - .data_o(resp_int1), .src_rdy_o(resp_src_rdy_int1), .dst_rdy_i(resp_dst_rdy_int1), - .wb_adr_o(wb_adr_o), .wb_dat_mosi(wb_dat_mosi), .wb_dat_miso(wb_dat_miso), .wb_sel_o(wb_sel_o), - .wb_cyc_o(wb_cyc_o), .wb_stb_o(wb_stb_o), .wb_we_o(wb_we_o), .wb_ack_i(wb_ack_i), - .triggers(triggers), - .debug0(), .debug1()); - - wire [18:0] tx_err19_data; - wire tx_err19_src_rdy, tx_err19_dst_rdy; - - fifo36_to_fifo19 #(.LE(1)) f36_to_f19_txerr - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .f36_datain(tx_err_data_i), .f36_src_rdy_i(tx_err_src_rdy_i), .f36_dst_rdy_o(tx_err_dst_rdy_o), - .f19_dataout(tx_err19_data), .f19_src_rdy_o(tx_err19_src_rdy), .f19_dst_rdy_i(tx_err19_dst_rdy) ); - - fifo19_mux #(.prio(0)) mux_err_stream - (.clk(wb_clk), .reset(wb_rst), .clear(0), - .data0_i(resp_int1), .src0_rdy_i(resp_src_rdy_int1), .dst0_rdy_o(resp_dst_rdy_int1), - .data1_i(tx_err19_data), .src1_rdy_i(tx_err19_src_rdy), .dst1_rdy_o(tx_err19_dst_rdy), - .data_o(resp_int2), .src_rdy_o(resp_src_rdy_int2), .dst_rdy_i(resp_dst_rdy_int2)); - - fifo19_pad #(.LENGTH(16)) fifo19_pad - (.clk(fifo_clk), .reset(fifo_rst), .clear(0), - .data_i(resp_int2), .src_rdy_i(resp_src_rdy_int2), .dst_rdy_o(resp_dst_rdy_int2), - .data_o(resp_data), .src_rdy_o(resp_src_rdy), .dst_rdy_i(resp_dst_rdy)); - - // //////////////////////////////////////////// - // DEBUG - - //assign debug0 = { rx19_src_rdy, rx19_dst_rdy, resp_src_rdy, resp_dst_rdy, gpif_ctl[3:0], gpif_rdy[3:0], - // gpif_d_copy[15:0] }; - - //assign debug1 = { { debug_rd[15:8] }, - // { debug_rd[7:0] }, - // { rx_src_rdy_i, rx_dst_rdy_o, rx36_src_rdy, rx36_dst_rdy, rx19_src_rdy, rx19_dst_rdy, resp_src_rdy, resp_dst_rdy}, - // { tx_src_rdy_o, tx_dst_rdy_i, tx19_src_rdy, tx19_dst_rdy, tx36_src_rdy, tx36_dst_rdy, ctrl_src_rdy, ctrl_dst_rdy} }; - - assign debug0 = { gpif_ctl[3:0], gpif_rdy[3:0], debug_split0[23:0] }; - assign debug1 = { gpif_misc[0], debug_rd[14:0], debug_split1[15:8], debug_split1[7:0] }; -endmodule // gpif diff --git a/fpga/usrp2/gpif/gpif_rd.v b/fpga/usrp2/gpif/gpif_rd.v deleted file mode 100644 index b05c3cfb6..000000000 --- a/fpga/usrp2/gpif/gpif_rd.v +++ /dev/null @@ -1,111 +0,0 @@ -// -// 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/>. -// - - -module gpif_rd - (input gpif_clk, input gpif_rst, - output [15:0] gpif_data, input gpif_rd, input gpif_ep, - output reg gpif_empty_d, output reg gpif_empty_c, - output reg gpif_flush, - - input sys_clk, input sys_rst, - input [18:0] data_i, input src_rdy_i, output dst_rdy_o, - input [18:0] resp_i, input resp_src_rdy_i, output resp_dst_rdy_o, - output [31:0] debug - ); - - wire [18:0] data_o; // occ bit indicates flush - wire [17:0] resp_o; // no occ bit - wire final_rdy_data, final_rdy_resp; - - // 33/257 Bug Fix - reg [8:0] read_count; - always @(negedge gpif_clk) - if(gpif_rst) - read_count <= 0; - else if(gpif_rd) - read_count <= read_count + 1; - else - read_count <= 0; - - // Data Path - wire [18:0] data_int; - wire src_rdy_int, dst_rdy_int; - fifo_2clock_cascade #(.WIDTH(19), .SIZE(4)) rd_fifo_2clk - (.wclk(sys_clk), .datain(data_i[18:0]), .src_rdy_i(src_rdy_i), .dst_rdy_o(dst_rdy_o), .space(), - .rclk(~gpif_clk), .dataout(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int), .occupied(), - .arst(sys_rst)); - - reg [7:0] packet_count; - wire consume_data_line = gpif_rd & ~gpif_ep & ~read_count[8]; - wire produce_eop = src_rdy_int & dst_rdy_int & data_int[17]; - wire consume_sop = consume_data_line & final_rdy_data & data_o[16]; - wire consume_eop = consume_data_line & final_rdy_data & data_o[17]; - - fifo_cascade #(.WIDTH(19), .SIZE(10)) rd_fifo - (.clk(~gpif_clk), .reset(gpif_rst), .clear(0), - .datain(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), .space(), - .dataout(data_o), .src_rdy_o(final_rdy_data), .dst_rdy_i(consume_data_line), .occupied()); - - always @(negedge gpif_clk) - if(gpif_rst) - packet_count <= 0; - else - if(produce_eop & ~consume_sop) - packet_count <= packet_count + 1; - else if(consume_sop & ~produce_eop) - packet_count <= packet_count - 1; - - always @(negedge gpif_clk) - if(gpif_rst) - gpif_empty_d <= 1; - else - gpif_empty_d <= ~|packet_count; - - // Use occ bit to signal a gpif flush - always @(negedge gpif_clk) - if(gpif_rst) - gpif_flush <= 0; - else if(consume_eop & data_o[18]) - gpif_flush <= ~gpif_flush; - - // Response Path - wire [15:0] resp_fifolevel; - wire consume_resp_line = gpif_rd & gpif_ep & ~read_count[4]; - - fifo_2clock_cascade #(.WIDTH(18), .SIZE(4)) resp_fifo_2clk - (.wclk(sys_clk), .datain(resp_i[17:0]), .src_rdy_i(resp_src_rdy_i), .dst_rdy_o(resp_dst_rdy_o), .space(), - .rclk(~gpif_clk), .dataout(resp_o), - .src_rdy_o(final_rdy_resp), .dst_rdy_i(consume_resp_line), .occupied(resp_fifolevel), - .arst(sys_rst)); - - // FIXME -- handle short packets - - always @(negedge gpif_clk) - if(gpif_rst) - gpif_empty_c <= 1; - else - gpif_empty_c <= resp_fifolevel < 16; - - // Output Mux - assign gpif_data = gpif_ep ? resp_o[15:0] : data_o[15:0]; - - assign debug = { { 16'd0 }, - { data_int[17:16], data_o[17:16], packet_count[3:0] }, - { consume_sop, consume_eop, final_rdy_data, data_o[18], consume_data_line, consume_resp_line, src_rdy_int, dst_rdy_int} }; - -endmodule // gpif_rd diff --git a/fpga/usrp2/gpif/gpif_tb.v b/fpga/usrp2/gpif/gpif_tb.v deleted file mode 100644 index 686284c2b..000000000 --- a/fpga/usrp2/gpif/gpif_tb.v +++ /dev/null @@ -1,142 +0,0 @@ -// -// 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/>. -// - - -module gpif_tb(); - - reg sys_clk = 0; - reg sys_rst = 1; - reg gpif_clk = 0; - reg gpif_rst = 1; - - reg [15:0] gpif_data; - reg WR = 0, EP = 0; - - wire CF, DF; - - wire gpif_full_d, gpif_full_c; - wire [18:0] data_o, ctrl_o, data_splt; - wire src_rdy, dst_rdy, src_rdy_splt, dst_rdy_splt; - wire ctrl_src_rdy, ctrl_dst_rdy; - - assign ctrl_dst_rdy = 1; - - initial $dumpfile("gpif_tb.vcd"); - initial $dumpvars(0,gpif_tb); - - initial #1000 gpif_rst = 0; - initial #1000 sys_rst = 0; - always #64 gpif_clk <= ~gpif_clk; - always #47.9 sys_clk <= ~sys_clk; - - wire [18:0] data_int; - wire src_rdy_int, dst_rdy_int; - - assign dst_rdy_splt = 1; - - gpif_wr gpif_write - (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), - .gpif_data(gpif_data), .gpif_wr(WR), .gpif_ep(EP), - .gpif_full_d(DF), .gpif_full_c(CF), - - .sys_clk(sys_clk), .sys_rst(sys_rst), - .data_o(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int), - .ctrl_o(ctrl_o), .ctrl_src_rdy_o(ctrl_src_rdy), .ctrl_dst_rdy_i(ctrl_dst_rdy) ); - - packet_reframer tx_packet_reframer - (.clk(sys_clk), .reset(sys_rst), .clear(0), - .data_i(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), - .data_o(data_o), .src_rdy_o(src_rdy), .dst_rdy_i(dst_rdy)); - - packet_splitter #(.FRAME_LEN(256)) rx_packet_splitter - (.clk(sys_clk), .reset(sys_rst), .clear(0), - .frames_per_packet(2), - .data_i(data_o), .src_rdy_i(src_rdy), .dst_rdy_o(dst_rdy), - .data_o(data_splt), .src_rdy_o(src_rdy_splt), .dst_rdy_i(dst_rdy_splt)); - - always @(posedge sys_clk) - if(ctrl_src_rdy & ctrl_dst_rdy) - $display("CTRL: %x",ctrl_o); - - always @(posedge sys_clk) - if(src_rdy_splt & dst_rdy_splt) - begin - if(data_splt[16]) - $display("<-------- DATA SOF--------->"); - $display("DATA: %x",data_splt); - if(data_splt[17]) - $display("<-------- DATA EOF--------->"); - end - - initial - begin - #10000; - repeat (1) - begin - @(posedge gpif_clk); - - WR <= 1; - gpif_data <= 256; // Length - @(posedge gpif_clk); - gpif_data <= 16'h00; - @(posedge gpif_clk); - repeat(254) - begin - gpif_data <= gpif_data + 1; - @(posedge gpif_clk); - end - WR <= 0; - - while(DF) - @(posedge gpif_clk); - repeat (16) - @(posedge gpif_clk); - - WR <= 1; - repeat(256) - begin - gpif_data <= gpif_data - 1; - @(posedge gpif_clk); - end - WR <= 0; - - -/* - while(DF) - @(posedge gpif_clk); - - repeat (20) - @(posedge gpif_clk); - WR <= 1; - gpif_data <= 16'h5; - @(posedge gpif_clk); - gpif_data <= 16'h00; - @(posedge gpif_clk); - repeat(254) - begin - gpif_data <= gpif_data - 1; - @(posedge gpif_clk); - end - WR <= 0; - */ - end - end // initial begin - - initial #200000 $finish; - - -endmodule // gpif_tb diff --git a/fpga/usrp2/gpif/gpif_wr.v b/fpga/usrp2/gpif/gpif_wr.v deleted file mode 100644 index 89fae282e..000000000 --- a/fpga/usrp2/gpif/gpif_wr.v +++ /dev/null @@ -1,95 +0,0 @@ -// -// 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/>. -// - - -module gpif_wr - (input gpif_clk, input gpif_rst, - input [15:0] gpif_data, input gpif_wr, input gpif_ep, - output reg gpif_full_d, output reg gpif_full_c, - - input sys_clk, input sys_rst, - output [18:0] data_o, output src_rdy_o, input dst_rdy_i, - output [18:0] ctrl_o, output ctrl_src_rdy_o, input ctrl_dst_rdy_i, - output [31:0] debug ); - - reg wr_reg, ep_reg; - reg [15:0] gpif_data_reg; - - always @(posedge gpif_clk) - begin - ep_reg <= gpif_ep; - wr_reg <= gpif_wr; - gpif_data_reg <= gpif_data; - end - - reg [9:0] write_count; - - always @(posedge gpif_clk) - if(gpif_rst) - write_count <= 0; - else if(wr_reg) - write_count <= write_count + 1; - else - write_count <= 0; - - reg sop; - wire eop = (write_count == 255); - wire eop_ctrl = (write_count == 15); - - always @(posedge gpif_clk) - sop <= gpif_wr & ~wr_reg; - - // Data Path - wire [15:0] fifo_space; - always @(posedge gpif_clk) - if(gpif_rst) - gpif_full_d <= 1; - else - gpif_full_d <= fifo_space < 256; - - wire [17:0] data_int; - wire src_rdy_int, dst_rdy_int; - - fifo_cascade #(.WIDTH(18), .SIZE(10)) wr_fifo - (.clk(gpif_clk), .reset(gpif_rst), .clear(0), - .datain({eop,sop,gpif_data_reg}), .src_rdy_i(~ep_reg & wr_reg & ~write_count[8]), .dst_rdy_o(), .space(fifo_space), - .dataout(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int), .occupied()); - - fifo_2clock_cascade #(.WIDTH(18), .SIZE(4)) wr_fifo_2clk - (.wclk(gpif_clk), .datain(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), .space(), - .rclk(sys_clk), .dataout(data_o[17:0]), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i), .occupied(), - .arst(sys_rst)); - assign data_o[18] = 1'b0; - - // Control Path - wire [15:0] ctrl_fifo_space; - always @(posedge gpif_clk) - if(gpif_rst) - gpif_full_c <= 1; - else - gpif_full_c <= ctrl_fifo_space < 16; - - fifo_2clock_cascade #(.WIDTH(19), .SIZE(4)) ctrl_fifo_2clk - (.wclk(gpif_clk), .datain({1'b0,eop_ctrl,sop,gpif_data_reg}), - .src_rdy_i(ep_reg & wr_reg & ~write_count[4]), .dst_rdy_o(), .space(ctrl_fifo_space), - .rclk(sys_clk), .dataout(ctrl_o[18:0]), - .src_rdy_o(ctrl_src_rdy_o), .dst_rdy_i(ctrl_dst_rdy_i), .occupied(), - .arst(sys_rst)); - - assign debug = { 16'd0, ep_reg, wr_reg, eop, sop, (~ep_reg & wr_reg & ~write_count[8]), src_rdy_int, dst_rdy_int, write_count[8:0]}; - -endmodule // gpif_wr diff --git a/fpga/usrp2/gpif/gpif_wr_tb.v b/fpga/usrp2/gpif/gpif_wr_tb.v deleted file mode 100644 index 171bb96a1..000000000 --- a/fpga/usrp2/gpif/gpif_wr_tb.v +++ /dev/null @@ -1,110 +0,0 @@ -// -// 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/>. -// - - -module gpif_wr_tb(); - - reg sys_clk = 0; - reg sys_rst = 1; - reg gpif_clk = 0; - reg gpif_rst = 1; - - reg [15:0] gpif_data; - reg WR = 0, EP = 0; - - wire CF, DF; - - wire gpif_full_d, gpif_full_c; - wire [18:0] data_o, ctrl_o; - wire src_rdy, dst_rdy; - wire ctrl_src_rdy, ctrl_dst_rdy; - - assign ctrl_dst_rdy = 1; - assign dst_rdy = 1; - - initial $dumpfile("gpif_wr_tb.vcd"); - initial $dumpvars(0,gpif_wr_tb); - - initial #1000 gpif_rst = 0; - initial #1000 sys_rst = 0; - always #64 gpif_clk <= ~gpif_clk; - always #47.9 sys_clk <= ~sys_clk; - - wire [18:0] data_int; - wire src_rdy_int, dst_rdy_int; - - gpif_wr gpif_write - (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), - .gpif_data(gpif_data), .gpif_wr(WR), .gpif_ep(EP), - .gpif_full_d(DF), .gpif_full_c(CF), - - .sys_clk(sys_clk), .sys_rst(sys_rst), - .data_o(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int), - .ctrl_o(ctrl_o), .ctrl_src_rdy_o(ctrl_src_rdy), .ctrl_dst_rdy_i(ctrl_dst_rdy) ); - - packet_reframer tx_packet_reframer - (.clk(sys_clk), .reset(sys_rst), .clear(0), - .data_i(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), - .data_o(data_o), .src_rdy_o(src_rdy), .dst_rdy_i(dst_rdy)); - - always @(posedge sys_clk) - if(ctrl_src_rdy & ctrl_dst_rdy) - $display("CTRL: %x",ctrl_o); - - always @(posedge sys_clk) - if(src_rdy & dst_rdy) - begin - if(data_o[16]) - $display("<-------- DATA SOF--------->"); - $display("DATA: %x",data_o); - if(data_o[17]) - $display("<-------- DATA EOF--------->"); - end - - initial - begin - #10000; - repeat (1) - begin - WR <= 1; - gpif_data <= 10; // Length - @(posedge gpif_clk); - gpif_data <= 16'h00; - @(posedge gpif_clk); - repeat(254) - begin - gpif_data <= gpif_data + 1; - @(posedge gpif_clk); - end - WR <= 0; - repeat (20) - @(posedge gpif_clk); - WR <= 1; - gpif_data <= 16'h5; - @(posedge gpif_clk); - repeat(254) - begin - gpif_data <= gpif_data - 1; - @(posedge gpif_clk); - end - end - end // initial begin - - initial #100000 $finish; - - -endmodule // gpif_wr_tb diff --git a/fpga/usrp2/gpif/gpmc16_to_fifo36.v b/fpga/usrp2/gpif/gpmc16_to_fifo36.v new file mode 100644 index 000000000..933891715 --- /dev/null +++ b/fpga/usrp2/gpif/gpmc16_to_fifo36.v @@ -0,0 +1,64 @@ +// +// Copyright 2012 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/>. +// + +module gpmc16_to_fifo36 +#( + parameter FIFO_SIZE = 9, + + //not ready until minimum xfers of space available + parameter MIN_SPACE16 = 128 +) +( + //input interface + input gpif_clk, input gpif_rst, + input [15:0] in_data, + input valid, + output reg ready, + + //output fifo interface + input fifo_clk, input fifo_rst, + output [35:0] out_data, + output out_src_rdy, + input out_dst_rdy +); + + wire [35:0] data_int; + wire src_rdy_int, dst_rdy_int; + wire [18:0] refr_data; + wire refr_src_rdy, refr_dst_rdy; + + wire [15:0] fifo_space; + + always @(posedge gpif_clk) + ready <= (fifo_space >= MIN_SPACE16/2); + + packet_reframer packet_reframer + (.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0), + .data_i(in_data), .src_rdy_i(valid), .dst_rdy_o(), + .data_o(refr_data), .src_rdy_o(refr_src_rdy), .dst_rdy_i(refr_dst_rdy)); + + fifo19_to_fifo36 #(.LE(1)) f19_to_f36 + (.clk(gpif_clk), .reset(gpif_rst), .clear(1'b0), + .f19_datain(refr_data), .f19_src_rdy_i(refr_src_rdy), .f19_dst_rdy_o(refr_dst_rdy), + .f36_dataout(data_int), .f36_src_rdy_o(src_rdy_int), .f36_dst_rdy_i(dst_rdy_int)); + + fifo_2clock_cascade #(.WIDTH(36), .SIZE(FIFO_SIZE)) fifo_2clk + (.wclk(gpif_clk), .datain(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), .space(fifo_space), + .rclk(fifo_clk), .dataout(out_data), .src_rdy_o(out_src_rdy), .dst_rdy_i(out_dst_rdy), .occupied(), + .arst(fifo_rst | gpif_rst)); + +endmodule //fifo_to_gpmc16 diff --git a/fpga/usrp2/gpif/lint b/fpga/usrp2/gpif/lint deleted file mode 100755 index 4316c89a9..000000000 --- a/fpga/usrp2/gpif/lint +++ /dev/null @@ -1,2 +0,0 @@ -iverilog -Wall -y . -y ../fifo/ -y ../control_lib/ -y ../models/ -y ../coregen/ -y ../simple_gemac/ -y ../sdr_lib/ -y ../vrt/ gpif.v 2>&1 | grep -v coregen | grep -v models - diff --git a/fpga/usrp2/gpif/packet_splitter.v b/fpga/usrp2/gpif/packet_splitter.v deleted file mode 100644 index ba4c8cded..000000000 --- a/fpga/usrp2/gpif/packet_splitter.v +++ /dev/null @@ -1,123 +0,0 @@ -// -// 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/>. -// - - -// Split vita packets longer than one GPIF frame, add padding on short frames - -module packet_splitter - #(parameter FRAME_LEN=256) - (input clk, input reset, input clear, - input [7:0] frames_per_packet, - input [18:0] data_i, - input src_rdy_i, - output dst_rdy_o, - output [18:0] data_o, - output src_rdy_o, - input dst_rdy_i, - output [31:0] debug0, - output [31:0] debug1); - - reg [1:0] state; - reg [15:0] length; - reg [15:0] frame_len; - reg [7:0] frame_count; - - localparam PS_IDLE = 0; - localparam PS_FRAME = 1; - localparam PS_NEW_FRAME = 2; - localparam PS_PAD = 3; - - wire eof_i = data_i[17]; - - always @(posedge clk) - if(reset | clear) - begin - state <= PS_IDLE; - frame_count <= 0; - end - else - case(state) - PS_IDLE : - if(src_rdy_i & dst_rdy_i) - begin - length <= { data_i[14:0],1'b0}; - frame_len <= FRAME_LEN; - state <= PS_FRAME; - frame_count <= 1; - end - PS_FRAME : - if(src_rdy_i & dst_rdy_i) - if((frame_len == 2) & ((length == 2) | eof_i)) - state <= PS_IDLE; - else if(frame_len == 2) - begin - length <= length - 1; - state <= PS_NEW_FRAME; - frame_count <= frame_count + 1; - end - else if((length == 2)|eof_i) - begin - frame_len <= frame_len - 1; - state <= PS_PAD; - end - else - begin - frame_len <= frame_len - 1; - length <= length - 1; - end - PS_NEW_FRAME : - if(src_rdy_i & dst_rdy_i) - begin - frame_len <= FRAME_LEN; - if((length == 2)|eof_i) - state <= PS_PAD; - else - begin - state <= PS_FRAME; - length <= length - 1; - end // else: !if((length == 2)|eof_i) - end // if (src_rdy_i & dst_rdy_i) - - PS_PAD : - if(dst_rdy_i) - if(frame_len == 2) - state <= PS_IDLE; - else - frame_len <= frame_len - 1; - - endcase // case (state) - - wire next_state_is_idle = dst_rdy_i & (frame_len==2) & - ( (state==PS_PAD) | ( (state==PS_FRAME) & src_rdy_i & ((length==2)|eof_i) ) ); - - - - - assign dst_rdy_o = dst_rdy_i & (state != PS_PAD); - assign src_rdy_o = src_rdy_i | (state == PS_PAD); - - wire eof_out = (frame_len == 2) & (state != PS_IDLE) & (state != PS_NEW_FRAME); - wire sof_out = (state == PS_IDLE) | (state == PS_NEW_FRAME); - wire occ_out = eof_out & next_state_is_idle & (frames_per_packet != frame_count); - - wire [15:0] data_out = data_i[15:0]; - assign data_o = {occ_out, eof_out, sof_out, data_out}; - - assign debug0 = { 8'd0, dst_rdy_o, src_rdy_o, next_state_is_idle, eof_out, sof_out, occ_out, state[1:0], frame_count[7:0], frames_per_packet[7:0] }; - assign debug1 = { length[15:0], frame_len[15:0] }; - -endmodule // packet_splitter diff --git a/fpga/usrp2/gpif/packet_splitter_tb.v b/fpga/usrp2/gpif/packet_splitter_tb.v deleted file mode 100644 index 329b58e0d..000000000 --- a/fpga/usrp2/gpif/packet_splitter_tb.v +++ /dev/null @@ -1,137 +0,0 @@ -// -// 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/>. -// - - -module packet_splitter_tb(); - - reg sys_clk = 0; - reg sys_rst = 1; - reg gpif_clk = 0; - reg gpif_rst = 1; - - reg [15:0] gpif_data; - reg WR = 0, EP = 0; - - wire CF, DF; - - wire gpif_full_d, gpif_full_c; - wire [18:0] data_o, ctrl_o, data_splt; - wire src_rdy, dst_rdy, src_rdy_splt, dst_rdy_splt; - wire ctrl_src_rdy, ctrl_dst_rdy; - - assign ctrl_dst_rdy = 1; - - initial $dumpfile("packet_splitter_tb.vcd"); - initial $dumpvars(0,packet_splitter_tb); - - initial #1000 gpif_rst = 0; - initial #1000 sys_rst = 0; - always #64 gpif_clk <= ~gpif_clk; - always #47.9 sys_clk <= ~sys_clk; - - wire [35:0] data_int; - wire src_rdy_int, dst_rdy_int; - - assign dst_rdy_splt = 1; - - vita_pkt_gen vita_pkt_gen - (.clk(sys_clk), .reset(sys_rst) , .clear(0), - .len(512),.data_o(data_int), .src_rdy_o(src_rdy_int), .dst_rdy_i(dst_rdy_int)); - - fifo36_to_fifo19 #(.LE(1)) f36_to_f19 - (.clk(sys_clk), .reset(sys_rst), .clear(0), - .f36_datain(data_int), .f36_src_rdy_i(src_rdy_int), .f36_dst_rdy_o(dst_rdy_int), - .f19_dataout(data_o), .f19_src_rdy_o(src_rdy), .f19_dst_rdy_i(dst_rdy)); - - packet_splitter #(.FRAME_LEN(13)) rx_packet_splitter - (.clk(sys_clk), .reset(sys_rst), .clear(0), - .frames_per_packet(4), - .data_i(data_o), .src_rdy_i(src_rdy), .dst_rdy_o(dst_rdy), - .data_o(data_splt), .src_rdy_o(src_rdy_splt), .dst_rdy_i(dst_rdy_splt)); - - always @(posedge sys_clk) - if(ctrl_src_rdy & ctrl_dst_rdy) - $display("CTRL: %x",ctrl_o); - - always @(posedge sys_clk) - if(src_rdy_splt & dst_rdy_splt) - begin - if(data_splt[16]) - $display("<-------- DATA SOF--------->"); - $display("DATA: %x",data_splt); - if(data_splt[17]) - $display("<-------- DATA EOF--------->"); - end - - initial - begin - #10000; - repeat (1) - begin - @(posedge gpif_clk); - - WR <= 1; - gpif_data <= 256; // Length - @(posedge gpif_clk); - gpif_data <= 16'h00; - @(posedge gpif_clk); - repeat(254) - begin - gpif_data <= gpif_data + 1; - @(posedge gpif_clk); - end - WR <= 0; - - while(DF) - @(posedge gpif_clk); - repeat (16) - @(posedge gpif_clk); - - WR <= 1; - repeat(256) - begin - gpif_data <= gpif_data - 1; - @(posedge gpif_clk); - end - WR <= 0; - - -/* - while(DF) - @(posedge gpif_clk); - - repeat (20) - @(posedge gpif_clk); - WR <= 1; - gpif_data <= 16'h5; - @(posedge gpif_clk); - gpif_data <= 16'h00; - @(posedge gpif_clk); - repeat(254) - begin - gpif_data <= gpif_data - 1; - @(posedge gpif_clk); - end - WR <= 0; - */ - end - end // initial begin - - initial #200000 $finish; - - -endmodule // packet_splitter_tb diff --git a/fpga/usrp2/gpif/slave_fifo.v b/fpga/usrp2/gpif/slave_fifo.v index e75f28913..0f301f8a6 100644 --- a/fpga/usrp2/gpif/slave_fifo.v +++ b/fpga/usrp2/gpif/slave_fifo.v @@ -20,85 +20,70 @@ //this is a FIFO master interface for the FX2 in "slave fifo" mode. module slave_fifo - #(parameter TXFIFOSIZE = 12, parameter RXFIFOSIZE = 12) + #( + //how many cycles max in a transfer state + parameter DATA_XFER_COUNT = 256, + parameter CTRL_XFER_COUNT = 32, + + //sizes for fifo36 2 clock cascade fifos + parameter DATA_RX_FIFO_SIZE = 9, + parameter DATA_TX_FIFO_SIZE = 9, + parameter CTRL_RX_FIFO_SIZE = 9, + parameter CTRL_TX_FIFO_SIZE = 9 + ) (// GPIF signals input gpif_clk, input gpif_rst, inout [15:0] gpif_d, input [3:0] gpif_ctl, - output sloe, output slrd, output slwr, output pktend, output [1:0] fifoadr, - - // Wishbone signals - input wb_clk, input wb_rst, - output [15:0] wb_adr_o, output [15:0] wb_dat_mosi, input [15:0] wb_dat_miso, - output [1:0] wb_sel_o, output wb_cyc_o, output wb_stb_o, output wb_we_o, input wb_ack_i, - input [7:0] triggers, - - input dsp_rx_run, - + output reg sloe, output reg slrd, output reg slwr, output reg pktend, output reg [1:0] fifoadr, + // FIFO interface - input fifo_clk, input fifo_rst, input clear_tx, input clear_rx, - output [35:0] tx_data_o, output tx_src_rdy_o, input tx_dst_rdy_i, - input [35:0] rx_data_i, input rx_src_rdy_i, output rx_dst_rdy_o, - input [35:0] tx_err_data_i, input tx_err_src_rdy_i, output tx_err_dst_rdy_o, - output tx_underrun, output rx_overrun, - - input [15:0] test_len, input [7:0] test_rate, input [3:0] test_ctrl, - output [31:0] debug0, output [31:0] debug1 + input fifo_clk, input fifo_rst, + output [35:0] tx_data, output tx_src_rdy, input tx_dst_rdy, + input [35:0] rx_data, input rx_src_rdy, output rx_dst_rdy, + output [35:0] ctrl_data, output ctrl_src_rdy, input ctrl_dst_rdy, + input [35:0] resp_data, input resp_src_rdy, output resp_dst_rdy, + + output [31:0] debug ); - reg FX2_DE, FX2_CE, FX2_DF, FX2_CF; + wire FX2_DE_pre = ~gpif_ctl[0]; //EP2 FX2 FIFO empty (FLAGA) + wire FX2_CE_pre = ~gpif_ctl[1]; //EP4 FX2 FIFO empty (FLAGB) + wire FX2_DF_pre = ~gpif_ctl[2]; //EP6 FX2 FIFO full (FLAGC) + wire FX2_CF_pre = ~gpif_ctl[3]; //EP8 FX2 FIFO full (FLAGD) - // inputs to FPGA (all active low) + reg FX2_DE, FX2_CE, FX2_DF, FX2_CF; always @(posedge gpif_clk) begin - FX2_DE <= ~gpif_ctl[0]; //EP2 FX2 FIFO empty (FLAGA) - FX2_CE <= ~gpif_ctl[1]; //EP4 FX2 FIFO empty (FLAGB) - FX2_DF <= ~gpif_ctl[2]; //EP6 FX2 FIFO full (FLAGC) - FX2_CF <= ~gpif_ctl[3]; //EP8 FX2 FIFO full (FLAGD) + FX2_DE <= FX2_DE_pre; //EP2 FX2 FIFO empty (FLAGA) + FX2_CE <= FX2_CE_pre; //EP4 FX2 FIFO empty (FLAGB) + FX2_DF <= FX2_DF_pre; //EP6 FX2 FIFO full (FLAGC) + FX2_CF <= FX2_CF_pre; //EP8 FX2 FIFO full (FLAGD) end - wire [17:0] gpif_d_out_ctrl, gpif_d_out_data, gpif_d_out; + wire [15:0] gpif_d_out_ctrl, gpif_d_out_data; + reg [15:0] gpif_d_out, gpif_d_in; // //////////////////////////////////////////////////////////////////// // GPIF bus master state machine - //transfer size for GPIF data. this can be anything really, it's specified only for - //fairness in bus sharing. 256 lines is 512 bytes over the wire, half the size of - //the double buffers in B100/B150. this should probably be a toplevel parameter or even - //a settings register value. - localparam data_transfer_size = 256; - localparam ctrl_transfer_size = 16; //probably unnecessary since ctrl xfers won't back up - - // state machine i/o to four fifos - //tx - wire ctrl_tx_dst_rdy; //sm input, ctrl tx path has space - wire ctrl_tx_src_rdy; //sm output, ctrl tx path enable - wire data_tx_dst_rdy; //sm input, data tx path has space - wire data_tx_src_rdy; //sm output, data tx path enable - - //rx - wire ctrl_rx_dst_rdy; //sm output, ctrl rx path enable - wire ctrl_rx_src_rdy; //sm input, ctrl rx path has space - wire data_rx_dst_rdy; //sm output, data rx path enable - wire data_rx_src_rdy; //sm input, data rx path has space - - reg tx_data_enough_space; + wire rx_valid, resp_valid; + reg tx_valid, ctrl_valid; + wire tx_ready, ctrl_ready; + reg rx_enable, resp_enable; reg [9:0] transfer_count; //number of lines (a line is 16 bits) in active transfer - - reg pktend_latch; reg [3:0] state; //state machine current state localparam STATE_IDLE = 0; - localparam STATE_DATA_RX = 5; + localparam STATE_THINK = 1; + localparam STATE_DATA_RX = 2; localparam STATE_DATA_TX = 3; - localparam STATE_CTRL_RX = 6; - localparam STATE_CTRL_TX = 9; - localparam STATE_DATA_TX_SLOE = 2; - localparam STATE_CTRL_TX_SLOE = 8; - localparam STATE_DATA_RX_ADR = 1; - localparam STATE_CTRL_RX_ADR = 4; - localparam STATE_PKTEND_ADR = 10; - localparam STATE_PKTEND = 7; + localparam STATE_CTRL_RX = 4; + localparam STATE_CTRL_TX = 5; + localparam STATE_DATA_TX_SLOE = 6; + localparam STATE_CTRL_TX_SLOE = 7; + localparam STATE_DATA_RX_ADR = 8; + localparam STATE_CTRL_RX_ADR = 9; //logs the last bus user for xfer fairness //we only care about data rx vs. tx since ctrl pkts are so short @@ -106,368 +91,184 @@ module slave_fifo localparam BUS_HOG_RX = 0; localparam BUS_HOG_TX = 1; + wire resp_eof; + reg [1:0] idle_count; + // ////////////////////////////////////////////////////////////// // FX2 slave FIFO bus master state machine // - always @(posedge gpif_clk) - if(gpif_rst) - state <= STATE_IDLE; - else - begin - case (state) - STATE_IDLE: - begin - transfer_count <= 0; - //handle transitions to other states - if(ctrl_tx_dst_rdy & ~FX2_CE) //if there's room in the ctrl fifo and the FX2 has ctrl data - state <= STATE_CTRL_TX_SLOE; - else if(ctrl_rx_src_rdy & ~FX2_CF) //if the ctrl fifo has data and the FX2 isn't full - state <= STATE_CTRL_RX_ADR; - else if(data_tx_dst_rdy & ~FX2_DE & last_data_bus_hog == BUS_HOG_RX & tx_data_enough_space) //if there's room in the data fifo and the FX2 has data - state <= STATE_DATA_TX_SLOE; - else if(data_rx_src_rdy & ~FX2_DF & last_data_bus_hog == BUS_HOG_TX) //if the data fifo has data and the FX2 isn't full - state <= STATE_DATA_RX_ADR; - else if(data_tx_dst_rdy & ~FX2_DE & tx_data_enough_space) - state <= STATE_DATA_TX_SLOE; - else if(data_rx_src_rdy & ~FX2_DF) - state <= STATE_DATA_RX_ADR; - else if(~data_rx_src_rdy & ~dsp_rx_run & pktend_latch & ~FX2_DF) - state <= STATE_PKTEND_ADR; - - if(data_rx_src_rdy) - pktend_latch <= 1; - end - - STATE_DATA_TX_SLOE: //just to assert SLOE one cycle before SLRD - state <= STATE_DATA_TX; - STATE_CTRL_TX_SLOE: - state <= STATE_CTRL_TX; - - STATE_DATA_RX_ADR: //just to assert FIFOADR one cycle before SLWR - state <= STATE_DATA_RX; - STATE_CTRL_RX_ADR: - state <= STATE_CTRL_RX; - - STATE_DATA_RX: - begin - if(data_rx_src_rdy && data_rx_dst_rdy) - transfer_count <= transfer_count + 1; - else - state <= STATE_IDLE; - last_data_bus_hog <= BUS_HOG_RX; - end - - STATE_PKTEND_ADR: - begin - state <= STATE_PKTEND; - end - - STATE_PKTEND: - begin - state <= STATE_IDLE; - pktend_latch <= 0; - end - - STATE_DATA_TX: - begin - if(data_tx_dst_rdy && data_tx_src_rdy) - transfer_count <= transfer_count + 1; - else - state <= STATE_IDLE; - last_data_bus_hog <= BUS_HOG_TX; - end - STATE_CTRL_RX: - begin - if(ctrl_rx_src_rdy && ctrl_rx_dst_rdy) - transfer_count <= transfer_count + 1; - else - state <= STATE_IDLE; - end - STATE_CTRL_TX: - begin - if(ctrl_tx_dst_rdy && ctrl_tx_src_rdy) - transfer_count <= transfer_count + 1; - else - state <= STATE_IDLE; - end - endcase + always @(posedge gpif_clk) + if(gpif_rst) begin + state <= STATE_IDLE; + sloe <= 1; + slrd <= 1; + slwr <= 1; + pktend <= 1; + rx_enable <= 0; + tx_valid <= 0; + ctrl_valid <= 0; + resp_enable <= 0; + idle_count <= 0; + end + else case (state) + STATE_IDLE: begin + transfer_count <= 0; + sloe <= 1; + slrd <= 1; + slwr <= 1; + pktend <= 1; + rx_enable <= 0; + tx_valid <= 0; + ctrl_valid <= 0; + resp_enable <= 0; + if (idle_count == 2'b11) state <= STATE_THINK; + idle_count <= idle_count + 1; + end + + STATE_THINK: begin + + idle_count <= 0; + + //handle transitions to other states + if(ctrl_ready & ~FX2_CE) begin //if there's room in the ctrl fifo and the FX2 has ctrl data + state <= STATE_CTRL_TX_SLOE; + fifoadr <= 2'b01; + sloe <= 0; + end + else if(resp_valid & ~FX2_CF) begin //if the ctrl fifo has data and the FX2 isn't full + state <= STATE_CTRL_RX_ADR; + fifoadr <= 2'b11; + end + else if(tx_ready & ~FX2_DE & last_data_bus_hog == BUS_HOG_RX) begin //if there's room in the data fifo and the FX2 has data + state <= STATE_DATA_TX_SLOE; + last_data_bus_hog <= BUS_HOG_TX; + fifoadr <= 2'b00; + sloe <= 0; end + else if(rx_valid & ~FX2_DF & last_data_bus_hog == BUS_HOG_TX) begin //if the data fifo has data and the FX2 isn't full + state <= STATE_DATA_RX_ADR; + last_data_bus_hog <= BUS_HOG_RX; + fifoadr <= 2'b10; + end + else if(tx_ready & ~FX2_DE) begin + state <= STATE_DATA_TX_SLOE; + last_data_bus_hog <= BUS_HOG_TX; + fifoadr <= 2'b00; + sloe <= 0; + end + else if(rx_valid & ~FX2_DF) begin + state <= STATE_DATA_RX_ADR; + last_data_bus_hog <= BUS_HOG_RX; + fifoadr <= 2'b10; + end + end - // /////////////////////////////////////////////////////////////////// - // fifo signal assignments and enables + STATE_DATA_TX_SLOE: begin //just to assert SLOE one cycle before SLRD + state <= STATE_DATA_TX; + slrd <= 0; + end - //enable fifos - assign data_rx_dst_rdy = (state == STATE_DATA_RX) && ~FX2_DF && (transfer_count != data_transfer_size); - assign data_tx_src_rdy = (state == STATE_DATA_TX) && ~FX2_DE && (transfer_count != data_transfer_size); - assign ctrl_rx_dst_rdy = (state == STATE_CTRL_RX) && ~FX2_CF; - assign ctrl_tx_src_rdy = (state == STATE_CTRL_TX) && ~FX2_CE; + STATE_CTRL_TX_SLOE: begin + state <= STATE_CTRL_TX; + slrd <= 0; + end - //framing for TX ctrl packets - wire sop_ctrl, eop_ctrl; - assign sop_ctrl = (transfer_count == 0); - assign eop_ctrl = (transfer_count == (ctrl_transfer_size-1)); + STATE_DATA_RX_ADR: begin //just to assert FIFOADR one cycle before SLWR + state <= STATE_DATA_RX; + rx_enable <= 1; + end + + STATE_CTRL_RX_ADR: begin + state <= STATE_CTRL_RX; + resp_enable <= 1; + end + + STATE_DATA_RX: begin + if (FX2_DF_pre || ~rx_valid || transfer_count == DATA_XFER_COUNT-1) begin + state <= STATE_IDLE; + rx_enable <= 0; + end + gpif_d_out <= gpif_d_out_data; + slwr <= ~rx_valid; + transfer_count <= transfer_count + 1; + end + + STATE_DATA_TX: begin + if (FX2_DE_pre || transfer_count == DATA_XFER_COUNT-1) begin + state <= STATE_IDLE; + slrd <= 1; + end + gpif_d_in <= gpif_d; + tx_valid <= 1; + transfer_count <= transfer_count + 1; + end + + STATE_CTRL_RX: begin + if (FX2_CF_pre || ~resp_valid || resp_eof || transfer_count == CTRL_XFER_COUNT-1) begin + state <= STATE_IDLE; + resp_enable <= 0; + end + pktend <= ~resp_eof; + gpif_d_out <= gpif_d_out_ctrl; + slwr <= ~resp_valid; + transfer_count <= transfer_count + 1; + end + + STATE_CTRL_TX: begin + if (FX2_CE_pre || transfer_count == CTRL_XFER_COUNT-1) begin + state <= STATE_IDLE; + slrd <= 1; + end + gpif_d_in <= gpif_d; + ctrl_valid <= 1; + transfer_count <= transfer_count + 1; + end + endcase - // //////////////////////////////////////////////////////////////////// - // set GPIF pins - - //set fifoadr to the appropriate endpoint - // {0,0}: EP2, data TX from host - // {0,1}: EP4, ctrl TX from host - // {1,0}: EP6, data RX to host - // {1,1}: EP8, ctrl RX to host - assign fifoadr = {(state == STATE_DATA_RX) | (state == STATE_CTRL_RX) | (state == STATE_DATA_RX_ADR) | (state == STATE_CTRL_RX_ADR) | (state == STATE_PKTEND) | (state == STATE_PKTEND_ADR), - (state == STATE_CTRL_RX) | (state == STATE_CTRL_RX_ADR) | (state == STATE_CTRL_TX) | (state == STATE_CTRL_TX_SLOE)}; - //set sloe, slwr, slrd (all active low) - //SLOE gets asserted when we want data from the FX2; i.e., TX mode - assign sloe = ~{(state == STATE_DATA_TX) | (state == STATE_CTRL_TX) | (state == STATE_DATA_TX_SLOE) | (state == STATE_CTRL_TX_SLOE)}; - //"read" and "write" here are from the master's point of view; - //so "read" means "transmit" and "write" means "receive" - assign slwr = ~{(data_rx_src_rdy && data_rx_dst_rdy) || (ctrl_rx_src_rdy && ctrl_rx_dst_rdy)}; - assign slrd = ~{(data_tx_src_rdy && data_tx_dst_rdy) || (ctrl_tx_src_rdy && ctrl_tx_dst_rdy)}; - - wire pktend_ctrl, pktend_data; - assign pktend_ctrl = ((~ctrl_rx_src_rdy | gpif_d_out_ctrl[17]) & (state == STATE_CTRL_RX)); - assign pktend_data = (state == STATE_PKTEND); - assign pktend = ~(pktend_ctrl | pktend_data); - - //mux between ctrl/data RX data out based on endpoint selection - assign gpif_d_out = fifoadr[0] ? gpif_d_out_ctrl : gpif_d_out_data; // GPIF output data lines, tristate - assign gpif_d = sloe ? gpif_d_out : 16'bz; + assign gpif_d = (sloe)? gpif_d_out[15:0] : 16'bz; // //////////////////////////////////////////////////////////////////// // TX Data Path - wire [15:0] txfifo_data; - wire txfifo_src_rdy, txfifo_dst_rdy; - wire [35:0] tx36_data; - wire tx36_src_rdy, tx36_dst_rdy; - wire [15:0] data_tx_2clk; - wire tx_src_rdy_2clk, tx_dst_rdy_2clk; - - wire [15:0] wr_fifo_space; - - always @(posedge gpif_clk) - tx_data_enough_space <= (wr_fifo_space >= data_transfer_size); - - fifo_cascade #(.WIDTH(16), .SIZE(12)) wr_fifo - (.clk(gpif_clk), .reset(gpif_rst), .clear(clear_tx), - .datain(gpif_d), .src_rdy_i(data_tx_src_rdy), .dst_rdy_o(data_tx_dst_rdy), .space(wr_fifo_space), - .dataout(txfifo_data), .src_rdy_o(txfifo_src_rdy), .dst_rdy_i(txfifo_dst_rdy), .occupied()); - - fifo_2clock_cascade #(.WIDTH(16), .SIZE(4)) wr_fifo_2clk - (.wclk(gpif_clk), .datain(txfifo_data), .src_rdy_i(txfifo_src_rdy), .dst_rdy_o(txfifo_dst_rdy), .space(), - .rclk(fifo_clk), .dataout(data_tx_2clk), .src_rdy_o(tx_src_rdy_2clk), .dst_rdy_i(tx_dst_rdy_2clk), .occupied(), - .arst(fifo_rst)); - - // join vita packets which are longer than one frame, add SOP/EOP/OCC - wire [18:0] refr_data; - wire refr_src_rdy, refr_dst_rdy; - //below 3 signals for debug only - wire refr_state; - wire refr_eof; - wire [15:0] refr_len; - - packet_reframer tx_packet_reframer - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .data_i(data_tx_2clk), .src_rdy_i(tx_src_rdy_2clk), .dst_rdy_o(tx_dst_rdy_2clk), - .data_o(refr_data), .src_rdy_o(refr_src_rdy), .dst_rdy_i(refr_dst_rdy), - .state(refr_state), .eof_out(refr_eof), .length(refr_len)); - - fifo19_to_fifo36 #(.LE(1)) f19_to_f36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .f19_datain(refr_data), .f19_src_rdy_i(refr_src_rdy), .f19_dst_rdy_o(refr_dst_rdy), - .f36_dataout(tx36_data), .f36_src_rdy_o(tx36_src_rdy), .f36_dst_rdy_i(tx36_dst_rdy)); - - fifo_cascade #(.WIDTH(36), .SIZE(TXFIFOSIZE)) tx_fifo36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .datain(tx36_data), .src_rdy_i(tx36_src_rdy), .dst_rdy_o(tx36_dst_rdy), - .dataout(tx_data_o), .src_rdy_o(tx_src_rdy_o), .dst_rdy_i(tx_dst_rdy_i)); + gpmc16_to_fifo36 #(.FIFO_SIZE(DATA_TX_FIFO_SIZE), .MIN_SPACE16(DATA_XFER_COUNT)) fifo36_to_gpmc16_tx( + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .in_data(gpif_d_in), .ready(tx_ready), .valid(tx_valid), + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .out_data(tx_data), .out_src_rdy(tx_src_rdy), .out_dst_rdy(tx_dst_rdy) + ); // //////////////////////////////////////////// // RX Data Path - wire [35:0] rx36_data; - wire rx36_src_rdy, rx36_dst_rdy; - wire [18:0] rx19_data; - wire rx19_src_rdy, rx19_dst_rdy; - wire [15:0] rxfifospace; - - //deep 36 bit wide input fifo buffers from DSP - fifo_cascade #(.WIDTH(36), .SIZE(8)) rx_fifo36 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .datain(rx_data_i), .src_rdy_i(rx_src_rdy_i), .dst_rdy_o(rx_dst_rdy_o), - .dataout(rx36_data), .src_rdy_o(rx36_src_rdy), .dst_rdy_i(rx36_dst_rdy)); - - //convert to fifo19 - fifo36_to_fifo19 #(.LE(1)) f36_to_f19 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .f36_datain(rx36_data), .f36_src_rdy_i(rx36_src_rdy), .f36_dst_rdy_o(rx36_dst_rdy), - .f19_dataout(rx19_data), .f19_src_rdy_o(rx19_src_rdy), .f19_dst_rdy_i(rx19_dst_rdy) ); - - wire [18:0] data_rx_int; - wire rx_src_rdy_int, rx_dst_rdy_int; - //clock domain crossing fifo for RX data - fifo_2clock_cascade #(.WIDTH(19), .SIZE(4)) rd_fifo_2clk - (.wclk(fifo_clk), .datain(rx19_data), .src_rdy_i(rx19_src_rdy), .dst_rdy_o(rx19_dst_rdy), .space(), - .rclk(~gpif_clk), .dataout(data_rx_int), .src_rdy_o(rx_src_rdy_int), .dst_rdy_i(rx_dst_rdy_int), .occupied(), - .arst(fifo_rst)); - - //rd_fifo buffers writes to the 2clock fifo above - fifo_cascade #(.WIDTH(16), .SIZE(RXFIFOSIZE)) rd_fifo - (.clk(~gpif_clk), .reset(gpif_rst), .clear(clear_rx), - .datain(data_rx_int), .src_rdy_i(rx_src_rdy_int), .dst_rdy_o(rx_dst_rdy_int), .space(rxfifospace), - .dataout(gpif_d_out_data), .src_rdy_o(data_rx_src_rdy), .dst_rdy_i(data_rx_dst_rdy), .occupied()); + fifo36_to_gpmc16 #(.FIFO_SIZE(DATA_RX_FIFO_SIZE)) fifo36_to_gpmc16_rx( + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .in_data(rx_data), .in_src_rdy(rx_src_rdy), .in_dst_rdy(rx_dst_rdy), + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .out_data(gpif_d_out_data), .valid(rx_valid), .enable(rx_enable) + ); // //////////////////////////////////////////////////////////////////// - // FIFO to Wishbone interface + // CTRL TX Data Path - wire [18:0] resp_data, resp_int; - wire resp_src_rdy, resp_dst_rdy; - wire resp_src_rdy_int, resp_dst_rdy_int; - - wire [18:0] tx_err19_data; - wire tx_err19_src_rdy, tx_err19_dst_rdy; - - wire [18:0] ctrl_data; - wire ctrl_src_rdy, ctrl_dst_rdy; - - fifo_to_wb fifo_to_wb - (.clk(fifo_clk), .reset(fifo_rst), .clear(0), - .data_i(ctrl_data), .src_rdy_i(ctrl_src_rdy), .dst_rdy_o(ctrl_dst_rdy), - .data_o(resp_int), .src_rdy_o(resp_src_rdy_int), .dst_rdy_i(resp_dst_rdy_int), - .wb_adr_o(wb_adr_o), .wb_dat_mosi(wb_dat_mosi), .wb_dat_miso(wb_dat_miso), .wb_sel_o(wb_sel_o), - .wb_cyc_o(wb_cyc_o), .wb_stb_o(wb_stb_o), .wb_we_o(wb_we_o), .wb_ack_i(wb_ack_i), - .triggers(triggers), - .debug0(), .debug1()); - - // //////////////////////////////////////////////////////////////////// - // TX CTRL PATH (ctrl commands into Wishbone) + gpmc16_to_fifo36 #(.FIFO_SIZE(CTRL_TX_FIFO_SIZE), .MIN_SPACE16(CTRL_XFER_COUNT)) fifo36_to_gpmc16_ctrl( + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .in_data(gpif_d_in), .ready(ctrl_ready), .valid(ctrl_valid), + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .out_data(ctrl_data), .out_src_rdy(ctrl_src_rdy), .out_dst_rdy(ctrl_dst_rdy) + ); - //how does this use fifo_clk instead of wb_clk - //answer: on b100 fifo clk IS wb clk - fifo_2clock_cascade #(.WIDTH(19), .SIZE(4)) ctrl_fifo_2clk - (.wclk(gpif_clk), .datain({1'b0,eop_ctrl,sop_ctrl,gpif_d}), - .src_rdy_i(ctrl_tx_src_rdy), .dst_rdy_o(ctrl_tx_dst_rdy), .space(), - .rclk(fifo_clk), .dataout(ctrl_data), - .src_rdy_o(ctrl_src_rdy), .dst_rdy_i(ctrl_dst_rdy), .occupied(), - .arst(fifo_rst)); + // //////////////////////////////////////////// + // CTRL RX Data Path + + fifo36_to_gpmc16 #(.FIFO_SIZE(CTRL_RX_FIFO_SIZE)) fifo36_to_gpmc16_resp( + .fifo_clk(fifo_clk), .fifo_rst(fifo_rst), + .in_data(resp_data), .in_src_rdy(resp_src_rdy), .in_dst_rdy(resp_dst_rdy), + .gpif_clk(gpif_clk), .gpif_rst(gpif_rst), + .out_data(gpif_d_out_ctrl), .valid(resp_valid), .enable(resp_enable), + .eof(resp_eof) + ); - // //////////////////////////////////////////////////////////////////// - // RX CTRL PATH (async packets, ctrl response data) - - //tx_err_data_i is the 36wide tx async err data clocked on fifo_clk - fifo36_to_fifo19 #(.LE(1)) f36_to_f19_txerr - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .f36_datain(tx_err_data_i), .f36_src_rdy_i(tx_err_src_rdy_i), .f36_dst_rdy_o(tx_err_dst_rdy_o), - .f19_dataout(tx_err19_data), .f19_src_rdy_o(tx_err19_src_rdy), .f19_dst_rdy_i(tx_err19_dst_rdy) ); - - //mux FIFO-to-WB along with async tx err pkts into one ctrl resp fifo - //how is this clocked on wb_clk? - fifo19_mux #(.prio(0)) mux_err_stream - (.clk(wb_clk), .reset(wb_rst), .clear(clear_rx), - .data0_i(resp_int), .src0_rdy_i(resp_src_rdy_int), .dst0_rdy_o(resp_dst_rdy_int), - .data1_i(tx_err19_data), .src1_rdy_i(tx_err19_src_rdy), .dst1_rdy_o(tx_err19_dst_rdy), - .data_o(resp_data), .src_rdy_o(resp_src_rdy), .dst_rdy_i(resp_dst_rdy)); - - //clock domain crossing cascade fifo for mux_err_stream to get from wb_clk to gpif_clk - //the output of this fifo is CTRL DATA PENDING FOR GPIF - fifo_2clock_cascade #(.WIDTH(18), .SIZE(4)) resp_fifo_2clk - (.wclk(wb_clk), .datain(resp_data[17:0]), .src_rdy_i(resp_src_rdy), .dst_rdy_o(resp_dst_rdy), .space(), - .rclk(~gpif_clk), .dataout(gpif_d_out_ctrl), - .src_rdy_o(ctrl_rx_src_rdy), .dst_rdy_i(ctrl_rx_dst_rdy), .occupied(), - .arst(wb_rst)); - - - // //////////////////////////////////////////////////////////////////// - // Debug support, timed and loopback - // RX side muxes test data into the same stream + assign debug = 0; - /////////////////////////////////////////////////////////////////////// - // debug lines - wire [31:0] debug_rd, debug_wr, debug_split0, debug_split1; - - wire [35:0] timedrx_data, loopbackrx_data, testrx_data; - wire [35:0] timedtx_data, loopbacktx_data, testtx_data; - wire timedrx_src_rdy, timedrx_dst_rdy, loopbackrx_src_rdy, loopbackrx_dst_rdy, - testrx_src_rdy, testrx_dst_rdy; - wire timedtx_src_rdy, timedtx_dst_rdy, loopbacktx_src_rdy, loopbacktx_dst_rdy, - testtx_src_rdy, testtx_dst_rdy; - wire timedrx_src_rdy_int, timedrx_dst_rdy_int, timedtx_src_rdy_int, timedtx_dst_rdy_int; - - wire [31:0] total, crc_err, seq_err, len_err; - wire sel_testtx = test_ctrl[0]; - wire sel_loopbacktx = test_ctrl[1]; - wire pkt_src_enable = test_ctrl[2]; - wire pkt_sink_enable = test_ctrl[3]; -/* - fifo36_mux rx_test_mux_lvl_1 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .data0_i(timedrx_data), .src0_rdy_i(timedrx_src_rdy), .dst0_rdy_o(timedrx_dst_rdy), - .data1_i(loopbackrx_data), .src1_rdy_i(loopbackrx_src_rdy), .dst1_rdy_o(loopbackrx_dst_rdy), - .data_o(testrx_data), .src_rdy_o(testrx_src_rdy), .dst_rdy_i(testrx_dst_rdy)); - - fifo36_mux rx_test_mux_lvl_2 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .data0_i(testrx_data), .src0_rdy_i(testrx_src_rdy), .dst0_rdy_o(testrx_dst_rdy), - .data1_i(rx_data_i), .src1_rdy_i(rx_src_rdy_i), .dst1_rdy_o(rx_dst_rdy_o), - .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); - - fifo_short #(.WIDTH(36)) loopback_fifo - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx | clear_rx), - .datain(loopbacktx_data), .src_rdy_i(loopbacktx_src_rdy), .dst_rdy_o(loopbacktx_dst_rdy), - .dataout(loopbackrx_data), .src_rdy_o(loopbackrx_src_rdy), .dst_rdy_i(loopbackrx_dst_rdy)); - - // Crossbar used as a demux for switching TX stream to main DSP or to test logic - crossbar36 tx_crossbar_lvl_1 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .cross(sel_testtx), - .data0_i(tx_data), .src0_rdy_i(tx_src_rdy), .dst0_rdy_o(tx_dst_rdy), - .data1_i(tx_data), .src1_rdy_i(1'b0), .dst1_rdy_o(), // No 2nd input - .data0_o(tx_data_o), .src0_rdy_o(tx_src_rdy_o), .dst0_rdy_i(tx_dst_rdy_i), - .data1_o(testtx_data), .src1_rdy_o(testtx_src_rdy), .dst1_rdy_i(testtx_dst_rdy) ); - - crossbar36 tx_crossbar_lvl_2 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .cross(sel_loopbacktx), - .data0_i(testtx_data), .src0_rdy_i(testtx_src_rdy), .dst0_rdy_o(testtx_dst_rdy), - .data1_i(testtx_data), .src1_rdy_i(1'b0), .dst1_rdy_o(), // No 2nd input - .data0_o(timedtx_data), .src0_rdy_o(timedtx_src_rdy), .dst0_rdy_i(timedtx_dst_rdy), - .data1_o(loopbacktx_data), .src1_rdy_o(loopbacktx_src_rdy), .dst1_rdy_i(loopbacktx_dst_rdy) ); - - // Fixed rate TX traffic consumer - fifo_pacer tx_pacer - (.clk(fifo_clk), .reset(fifo_rst), .rate(test_rate), .enable(pkt_sink_enable), - .src1_rdy_i(timedtx_src_rdy), .dst1_rdy_o(timedtx_dst_rdy), - .src2_rdy_o(timedtx_src_rdy_int), .dst2_rdy_i(timedtx_dst_rdy_int), - .underrun(tx_underrun), .overrun()); - - packet_verifier32 pktver32 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .data_i(timedtx_data), .src_rdy_i(timedtx_src_rdy_int), .dst_rdy_o(timedtx_dst_rdy_int), - .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); - - // Fixed rate RX traffic generator - vita_pkt_gen pktgen - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .len(test_len), - .data_o(timedrx_data), .src_rdy_o(timedrx_src_rdy_int), .dst_rdy_i(timedrx_dst_rdy_int)); - - fifo_pacer rx_pacer - (.clk(fifo_clk), .reset(fifo_rst), .rate(test_rate), .enable(pkt_src_enable), - .src1_rdy_i(timedrx_src_rdy_int), .dst1_rdy_o(timedrx_dst_rdy_int), - .src2_rdy_o(timedrx_src_rdy), .dst2_rdy_i(timedrx_dst_rdy), - .underrun(), .overrun(rx_overrun)); -*/ - // //////////////////////////////////////////// - // DEBUG - - assign debug0 = { pktend_latch, data_rx_src_rdy, gpif_ctl[3:0], sloe, slrd, slwr, pktend, fifoadr[1:0], state[3:0], gpif_d[15:0]}; - //assign debug0 = { data_tx_src_rdy, data_tx_dst_rdy, tx_src_rdy_int, tx_dst_rdy_int, - // tx19_src_rdy, tx19_dst_rdy, refr_src_rdy, refr_dst_rdy, - // tx36_src_rdy, tx36_dst_rdy, - // gpif_ctl[3:0], fifoadr[1:0], - // wr_fifo_space[15:0]}; - assign debug1 = { 16'b0, transfer_count[7:0], ctrl_rx_src_rdy, ctrl_tx_dst_rdy, data_rx_src_rdy, - data_tx_dst_rdy, ctrl_tx_src_rdy, ctrl_rx_dst_rdy, data_tx_src_rdy, data_rx_dst_rdy}; endmodule // slave_fifo diff --git a/fpga/usrp2/gpmc/Makefile.srcs b/fpga/usrp2/gpmc/Makefile.srcs index 4c6a1b4a2..39b37db58 100644 --- a/fpga/usrp2/gpmc/Makefile.srcs +++ b/fpga/usrp2/gpmc/Makefile.srcs @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2012 Ettus Research LLC # ################################################## @@ -10,5 +10,4 @@ cross_clock_reader.v \ fifo_to_gpmc.v \ gpmc.v \ gpmc_to_fifo.v \ -gpmc_wb.v \ )) diff --git a/fpga/usrp2/gpmc/cross_clock_reader.v b/fpga/usrp2/gpmc/cross_clock_reader.v index a8366badc..b4cdb79c5 100644 --- a/fpga/usrp2/gpmc/cross_clock_reader.v +++ b/fpga/usrp2/gpmc/cross_clock_reader.v @@ -27,18 +27,20 @@ module cross_clock_reader ); reg [WIDTH-1:0] shadow0, shadow1; + reg [2:0] count; always @(posedge clk) begin if (rst) begin out <= DEFAULT; shadow0 <= DEFAULT; shadow1 <= DEFAULT; + count <= 0; end - else if (shadow0 == shadow1) begin - out <= shadow1; - end + else if (shadow0 == shadow1) count <= count + 1; + else count <= 0; shadow0 <= in; shadow1 <= shadow0; + if (count == 3'b111) out <= shadow1; end endmodule //cross_clock_reader diff --git a/fpga/usrp2/gpmc/fifo_to_gpmc.v b/fpga/usrp2/gpmc/fifo_to_gpmc.v index 42c71d2d6..26443a702 100644 --- a/fpga/usrp2/gpmc/fifo_to_gpmc.v +++ b/fpga/usrp2/gpmc/fifo_to_gpmc.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -39,7 +39,7 @@ //////////////////////////////////////////////////////////////////////// module fifo_to_gpmc - #(parameter PTR_WIDTH = 2, parameter ADDR_WIDTH = 10) + #(parameter PTR_WIDTH = 2, parameter ADDR_WIDTH = 10, parameter LAST_ADDR = 10'h3ff) (input clk, input reset, input clear, input arst, input [17:0] data_i, input src_rdy_i, output dst_rdy_o, output [15:0] EM_D, input [ADDR_WIDTH:1] EM_A, input EM_CLK, input EM_OE, @@ -82,14 +82,15 @@ module fifo_to_gpmc end GPMC_STATE_EMPTY: begin - if (EM_A == 10'h3ff) begin + if (addr == LAST_ADDR) begin gpmc_state <= GPMC_STATE_START; gpmc_ptr <= next_gpmc_ptr; + addr <= 0; end end endcase //gpmc_state - end //EM_WE + end //EM_OE end //always //------------------------------------------------------------------ diff --git a/fpga/usrp2/gpmc/gpmc.v b/fpga/usrp2/gpmc/gpmc.v index a5d4db466..2ba69d184 100644 --- a/fpga/usrp2/gpmc/gpmc.v +++ b/fpga/usrp2/gpmc/gpmc.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -18,7 +18,7 @@ ////////////////////////////////////////////////////////////////////////////////// module gpmc - #(parameter TXFIFOSIZE = 11, + #(parameter TXFIFOSIZE = 11, parameter RXFIFOSIZE = 11, parameter ADDR_WIDTH = 10, parameter BUSDEBUG = 1) @@ -26,57 +26,51 @@ module gpmc input arst, input EM_CLK, inout [15:0] EM_D, input [ADDR_WIDTH:1] EM_A, input [1:0] EM_NBE, input EM_WAIT0, input EM_NCS4, input EM_NCS6, input EM_NWE, input EM_NOE, - + // GPIOs for FIFO signalling - output rx_have_data, output tx_have_space, - - // Wishbone signals - input wb_clk, input wb_rst, - output [ADDR_WIDTH:0] wb_adr_o, output [15:0] wb_dat_mosi, input [15:0] wb_dat_miso, - output [1:0] wb_sel_o, output wb_cyc_o, output wb_stb_o, output wb_we_o, input wb_ack_i, - + output rx_have_data, output tx_have_space, output resp_have_data, + // FIFO interface - input fifo_clk, input fifo_rst, input clear_tx, input clear_rx, - output [35:0] tx_data_o, output tx_src_rdy_o, input tx_dst_rdy_i, - input [35:0] rx_data_i, input rx_src_rdy_i, output rx_dst_rdy_o, + input fifo_clk, input fifo_rst, + output [35:0] tx_data, output tx_src_rdy, input tx_dst_rdy, + input [35:0] rx_data, input rx_src_rdy, output rx_dst_rdy, + output [35:0] ctrl_data, output ctrl_src_rdy, input ctrl_dst_rdy, + input [35:0] resp_data, input resp_src_rdy, output resp_dst_rdy, - output tx_underrun, output rx_overrun, - input [7:0] test_rate, input [3:0] test_ctrl, output [31:0] debug ); - wire EM_output_enable = (~EM_NOE & (~EM_NCS4 | ~EM_NCS6)); - wire [15:0] EM_D_fifo; - wire [15:0] EM_D_wb; + wire EM_output_enable = (~EM_NOE & (~EM_NCS4 | ~EM_NCS6)); + wire [15:0] EM_D_data; + wire [15:0] EM_D_ctrl; - assign EM_D = ~EM_output_enable ? 16'bz : ~EM_NCS4 ? EM_D_fifo : EM_D_wb; + assign EM_D = ~EM_output_enable ? 16'bz : ~EM_NCS4 ? EM_D_data : EM_D_ctrl; // CS4 is RAM_2PORT for DATA PATH (high-speed data) // Writes go into one RAM, reads come from the other - // CS6 is for CONTROL PATH (wishbone) + // CS6 is for CONTROL PATH (slow) // //////////////////////////////////////////// // TX Data Path wire [17:0] tx18_data; wire tx18_src_rdy, tx18_dst_rdy; - wire [35:0] tx_data, txb_data; - wire tx_src_rdy, tx_dst_rdy; + wire [35:0] txb_data; wire txb_src_rdy, txb_dst_rdy; gpmc_to_fifo #(.ADDR_WIDTH(ADDR_WIDTH)) gpmc_to_fifo (.EM_D(EM_D), .EM_A(EM_A), .EM_CLK(EM_CLK), .EM_WE(~EM_NCS4 & ~EM_NWE), - .clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), .arst(fifo_rst | clear_tx | arst), + .clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .arst(fifo_rst | arst), .data_o(tx18_data), .src_rdy_o(tx18_src_rdy), .dst_rdy_i(tx18_dst_rdy), .have_space(tx_have_space)); fifo19_to_fifo36 #(.LE(1)) f19_to_f36 // Little endian because ARM is LE - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), + (.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .f19_datain({1'b0,tx18_data}), .f19_src_rdy_i(tx18_src_rdy), .f19_dst_rdy_o(tx18_dst_rdy), .f36_dataout(txb_data), .f36_src_rdy_o(txb_src_rdy), .f36_dst_rdy_i(txb_dst_rdy)); fifo_cascade #(.WIDTH(36), .SIZE(TXFIFOSIZE)) tx_buffering( - .clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), + .clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .datain(txb_data), .src_rdy_i(txb_src_rdy), .dst_rdy_o(txb_dst_rdy), .dataout(tx_data), .src_rdy_o(tx_src_rdy), .dst_rdy_i(tx_dst_rdy) ); @@ -86,74 +80,88 @@ module gpmc wire [17:0] rx18_data; wire rx18_src_rdy, rx18_dst_rdy; - wire [35:0] rx_data, rxb_data; - wire rx_src_rdy, rx_dst_rdy; + wire [35:0] rxb_data; wire rxb_src_rdy, rxb_dst_rdy; wire dummy; fifo_cascade #(.WIDTH(36), .SIZE(RXFIFOSIZE)) rx_buffering( - .clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), + .clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .datain(rx_data), .src_rdy_i(rx_src_rdy), .dst_rdy_o(rx_dst_rdy), .dataout(rxb_data), .src_rdy_o(rxb_src_rdy), .dst_rdy_i(rxb_dst_rdy) ); fifo36_to_fifo19 #(.LE(1)) f36_to_f19 // Little endian because ARM is LE - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), + (.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .f36_datain(rxb_data), .f36_src_rdy_i(rxb_src_rdy), .f36_dst_rdy_o(rxb_dst_rdy), .f19_dataout({dummy,rx18_data}), .f19_src_rdy_o(rx18_src_rdy), .f19_dst_rdy_i(rx18_dst_rdy) ); - fifo_to_gpmc #(.ADDR_WIDTH(ADDR_WIDTH)) fifo_to_gpmc - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), .arst(fifo_rst | clear_rx | arst), + fifo_to_gpmc #(.ADDR_WIDTH(ADDR_WIDTH), .LAST_ADDR(10'h3ff)) fifo_to_gpmc + (.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .arst(fifo_rst | arst), .data_i(rx18_data), .src_rdy_i(rx18_src_rdy), .dst_rdy_o(rx18_dst_rdy), - .EM_D(EM_D_fifo), .EM_A(EM_A), .EM_CLK(EM_CLK), .EM_OE(~EM_NCS4 & ~EM_NOE), + .EM_D(EM_D_data), .EM_A(EM_A), .EM_CLK(EM_CLK), .EM_OE(~EM_NCS4 & ~EM_NOE), .data_available(rx_have_data)); // //////////////////////////////////////////// // Control path on CS6 - - gpmc_wb gpmc_wb - (.EM_CLK(EM_CLK), .EM_D_in(EM_D), .EM_D_out(EM_D_wb), .EM_A(EM_A), .EM_NBE(EM_NBE), - .EM_WE(~EM_NCS6 & ~EM_NWE), .EM_OE(~EM_NCS6 & ~EM_NOE), - .wb_clk(wb_clk), .wb_rst(wb_rst), - .wb_adr_o(wb_adr_o), .wb_dat_mosi(wb_dat_mosi), .wb_dat_miso(wb_dat_miso), - .wb_sel_o(wb_sel_o), .wb_cyc_o(wb_cyc_o), .wb_stb_o(wb_stb_o), .wb_we_o(wb_we_o), - .wb_ack_i(wb_ack_i) ); + + // //////////////////////////////////////////////////////////////////// + // CTRL TX Data Path + + wire [17:0] ctrl18_data; + wire ctrl18_src_rdy, ctrl18_dst_rdy; + wire [35:0] ctrlb_data; + wire ctrlb_src_rdy, ctrlb_dst_rdy; + + gpmc_to_fifo #(.PTR_WIDTH(5), .ADDR_WIDTH(5)) ctrl_gpmc_to_fifo + (.EM_D(EM_D), .EM_A(EM_A[5:1]), .EM_CLK(EM_CLK), .EM_WE(~EM_NCS6 & ~EM_NWE & (EM_A[ADDR_WIDTH:6] == 0)), + .clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .arst(fifo_rst | arst), + .data_o(ctrl18_data), .src_rdy_o(ctrl18_src_rdy), .dst_rdy_i(ctrl18_dst_rdy), + .have_space(/*always*/)); + + fifo19_to_fifo36 #(.LE(1)) ctrl_f19_to_f36 // Little endian because ARM is LE + (.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), + .f19_datain({1'b0,ctrl18_data}), .f19_src_rdy_i(ctrl18_src_rdy), .f19_dst_rdy_o(ctrl18_dst_rdy), + .f36_dataout(ctrlb_data), .f36_src_rdy_o(ctrlb_src_rdy), .f36_dst_rdy_i(ctrlb_dst_rdy)); + + fifo_cascade #(.WIDTH(36), .SIZE(9)) ctrl_buffering( + .clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), + .datain(ctrlb_data), .src_rdy_i(ctrlb_src_rdy), .dst_rdy_o(ctrlb_dst_rdy), + .dataout(ctrl_data), .src_rdy_o(ctrl_src_rdy), .dst_rdy_i(ctrl_dst_rdy) + ); // //////////////////////////////////////////// - // Test support, traffic generator, loopback, etc. - - // RX side muxes test data into the same stream - wire [35:0] loopbackrx_data, testrx_data; - wire [35:0] loopbacktx_data, testtx_data; - wire loopbackrx_src_rdy, loopbackrx_dst_rdy; - wire loopbacktx_src_rdy, loopbacktx_dst_rdy; - wire sel_testtx = test_ctrl[0]; - - fifo36_mux rx_test_mux_lvl_2 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_rx), - .data0_i(loopbackrx_data), .src0_rdy_i(loopbackrx_src_rdy), .dst0_rdy_o(loopbackrx_dst_rdy), - .data1_i(rx_data_i), .src1_rdy_i(rx_src_rdy_i), .dst1_rdy_o(rx_dst_rdy_o), - .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); - - fifo_short #(.WIDTH(36)) loopback_fifo - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx | clear_rx), - .datain(loopbacktx_data), .src_rdy_i(loopbacktx_src_rdy), .dst_rdy_o(loopbacktx_dst_rdy), - .dataout(loopbackrx_data), .src_rdy_o(loopbackrx_src_rdy), .dst_rdy_i(loopbackrx_dst_rdy)); - - // Crossbar used as a demux for switching TX stream to main DSP or to test logic - crossbar36 tx_crossbar_lvl_1 - (.clk(fifo_clk), .reset(fifo_rst), .clear(clear_tx), - .cross(sel_testtx), - .data0_i(tx_data), .src0_rdy_i(tx_src_rdy), .dst0_rdy_o(tx_dst_rdy), - .data1_i(tx_data), .src1_rdy_i(1'b0), .dst1_rdy_o(), // No 2nd input - .data0_o(tx_data_o), .src0_rdy_o(tx_src_rdy_o), .dst0_rdy_i(tx_dst_rdy_i), - .data1_o(loopbacktx_data), .src1_rdy_o(loopbacktx_src_rdy), .dst1_rdy_i(loopbacktx_dst_rdy) ); - - assign debug = { - EM_D, //16 + // CTRL RX Data Path + + wire [17:0] resp18_data; + wire resp18_src_rdy, resp18_dst_rdy; + wire [35:0] respb_data; + wire respb_src_rdy, respb_dst_rdy; + wire resp_dummy; + + fifo_cascade #(.WIDTH(36), .SIZE(9)) resp_buffering( + .clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), + .datain(resp_data), .src_rdy_i(resp_src_rdy), .dst_rdy_o(resp_dst_rdy), + .dataout(respb_data), .src_rdy_o(respb_src_rdy), .dst_rdy_i(respb_dst_rdy) + ); + + fifo36_to_fifo19 #(.LE(1)) resp_f36_to_f19 // Little endian because ARM is LE + (.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), + .f36_datain(respb_data), .f36_src_rdy_i(respb_src_rdy), .f36_dst_rdy_o(respb_dst_rdy), + .f19_dataout({resp_dummy,resp18_data}), .f19_src_rdy_o(resp18_src_rdy), .f19_dst_rdy_i(resp18_dst_rdy) ); + + fifo_to_gpmc #(.ADDR_WIDTH(ADDR_WIDTH), .LAST_ADDR(10'h00f)) resp_fifo_to_gpmc + (.clk(fifo_clk), .reset(fifo_rst), .clear(1'b0), .arst(fifo_rst | arst), + .data_i(resp18_data), .src_rdy_i(resp18_src_rdy), .dst_rdy_o(resp18_dst_rdy), + .EM_D(EM_D_ctrl), .EM_A(EM_A), .EM_CLK(EM_CLK), .EM_OE(~EM_NCS6 & ~EM_NOE), + .data_available(resp_have_data)); + + assign debug = { + EM_D, + //resp18_data[15:0], //16 EM_A, //10 - EM_CLK, EM_NCS4, EM_NWE, EM_NOE, //4 - EM_NCS6, wb_ack_i - }; + //resp18_data[17:16], resp18_src_rdy, resp18_dst_rdy, //4 + EM_NCS4, EM_NCS6, EM_NWE, EM_NOE, //4 + EM_CLK, resp_have_data //2 + }; endmodule // gpmc diff --git a/fpga/usrp2/gpmc/gpmc_to_fifo.v b/fpga/usrp2/gpmc/gpmc_to_fifo.v index cfc5aaa8b..3932b81ac 100644 --- a/fpga/usrp2/gpmc/gpmc_to_fifo.v +++ b/fpga/usrp2/gpmc/gpmc_to_fifo.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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,7 +22,7 @@ // The GPMC is asynchronously alerted when a BRAM page is available. // // EM_CLK: -// A GPMC read transaction consists of one EM_CLK cycle (idle low). +// A GPMC write transaction consists of one EM_CLK cycle (idle low). // // EM_WE: // Write enable is actually the combination of ~NWE & ~NCS. @@ -36,7 +36,7 @@ //////////////////////////////////////////////////////////////////////// module gpmc_to_fifo - #(parameter PTR_WIDTH = 2, parameter ADDR_WIDTH = 10) + #(parameter PTR_WIDTH = 2, parameter ADDR_WIDTH = 10, parameter XFER_OFFSET = 2) (input [15:0] EM_D, input [ADDR_WIDTH:1] EM_A, input EM_CLK, input EM_WE, input clk, input reset, input clear, input arst, output [17:0] data_o, output src_rdy_o, input dst_rdy_i, @@ -45,17 +45,20 @@ module gpmc_to_fifo //states for the GPMC side of things wire [17:0] data_i; reg gpmc_state; - reg [ADDR_WIDTH:1] last_addr; + reg [15:0] vita_len; + reg [ADDR_WIDTH:1] addr; + wire [ADDR_WIDTH:1] last_addr = {vita_len[ADDR_WIDTH-2:0], 1'b0} - 1'b1 + XFER_OFFSET; reg [PTR_WIDTH:0] gpmc_ptr, next_gpmc_ptr; localparam GPMC_STATE_START = 0; localparam GPMC_STATE_FILL = 1; //states for the FIFO side of things - reg fifo_state; + reg [1:0] fifo_state; reg [ADDR_WIDTH-1:0] counter; reg [PTR_WIDTH:0] fifo_ptr; localparam FIFO_STATE_CLAIM = 0; localparam FIFO_STATE_EMPTY = 1; + localparam FIFO_STATE_PRE = 2; //------------------------------------------------------------------ // State machine to control the data from GPMC to BRAM @@ -65,14 +68,16 @@ module gpmc_to_fifo gpmc_state <= GPMC_STATE_START; gpmc_ptr <= 0; next_gpmc_ptr <= 0; + addr <= 0; end else if (EM_WE) begin + addr <= EM_A + 1; case(gpmc_state) GPMC_STATE_START: begin - if (EM_A == 2) begin + if (data_i[16]) begin gpmc_state <= GPMC_STATE_FILL; - last_addr <= {EM_D[ADDR_WIDTH-2:0], 1'b0} - 1'b1 + 2; + vita_len <= EM_D; next_gpmc_ptr <= gpmc_ptr + 1; end end @@ -81,6 +86,7 @@ module gpmc_to_fifo if (data_i[17]) begin gpmc_state <= GPMC_STATE_START; gpmc_ptr <= next_gpmc_ptr; + addr <= 0; end end @@ -105,6 +111,7 @@ module gpmc_to_fifo cross_clock_reader #(.WIDTH(PTR_WIDTH+1)) read_next_gpmc_ptr (.clk(clk), .rst(reset | clear), .in(next_gpmc_ptr), .out(safe_next_gpmc_ptr)); + wire [PTR_WIDTH:0] fifo_ptr_next = fifo_ptr + 1; always @(posedge clk) if (reset | clear) have_space <= 0; else have_space <= (fifo_ptr ^ (1 << PTR_WIDTH)) != safe_next_gpmc_ptr; @@ -116,22 +123,28 @@ module gpmc_to_fifo if (reset | clear) begin fifo_state <= FIFO_STATE_CLAIM; fifo_ptr <= 0; - counter <= 2; + counter <= XFER_OFFSET; end else begin case(fifo_state) FIFO_STATE_CLAIM: begin - if (bram_available_to_empty) fifo_state <= FIFO_STATE_EMPTY; - counter <= 2; + if (bram_available_to_empty && data_o[16]) fifo_state <= FIFO_STATE_PRE; + counter <= XFER_OFFSET; + end + + FIFO_STATE_PRE: begin + fifo_state <= FIFO_STATE_EMPTY; + counter <= counter + 1; end FIFO_STATE_EMPTY: begin if (src_rdy_o && dst_rdy_i && data_o[17]) begin fifo_state <= FIFO_STATE_CLAIM; fifo_ptr <= fifo_ptr + 1; + counter <= XFER_OFFSET; end - if (src_rdy_o && dst_rdy_i) begin + else if (src_rdy_o && dst_rdy_i) begin counter <= counter + 1; end end @@ -140,18 +153,20 @@ module gpmc_to_fifo end end //always + wire enable = (fifo_state != FIFO_STATE_EMPTY) || dst_rdy_i; + assign src_rdy_o = fifo_state == FIFO_STATE_EMPTY; //assign data and frame bits to bram input assign data_i[15:0] = EM_D; - assign data_i[16] = (gpmc_state == GPMC_STATE_START); - assign data_i[17] = (EM_A == last_addr); + assign data_i[16] = (addr == XFER_OFFSET); + assign data_i[17] = (addr == last_addr); //instantiate dual ported bram for async read + write ram_2port #(.DWIDTH(18),.AWIDTH(PTR_WIDTH + ADDR_WIDTH)) async_fifo_bram (.clka(~EM_CLK),.ena(1'b1),.wea(EM_WE), - .addra({gpmc_ptr[PTR_WIDTH-1:0], EM_A}),.dia(data_i),.doa(), - .clkb(~clk),.enb(1'b1),.web(1'b0), + .addra({gpmc_ptr[PTR_WIDTH-1:0], addr}),.dia(data_i),.doa(), + .clkb(clk),.enb(enable),.web(1'b0), .addrb({fifo_ptr[PTR_WIDTH-1:0], counter}),.dib(18'h3ffff),.dob(data_o)); endmodule // gpmc_to_fifo diff --git a/fpga/usrp2/gpmc/gpmc_wb.v b/fpga/usrp2/gpmc/gpmc_wb.v deleted file mode 100644 index 4d368ca94..000000000 --- a/fpga/usrp2/gpmc/gpmc_wb.v +++ /dev/null @@ -1,79 +0,0 @@ -// -// 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/>. -// - - - -module gpmc_wb - (input EM_CLK, input [15:0] EM_D_in, output [15:0] EM_D_out, input [10:1] EM_A, input [1:0] EM_NBE, - input EM_WE, input EM_OE, - - input wb_clk, input wb_rst, - output reg [10:0] wb_adr_o, output reg [15:0] wb_dat_mosi, input [15:0] wb_dat_miso, - output reg [1:0] wb_sel_o, output wb_cyc_o, output reg wb_stb_o, output reg wb_we_o, input wb_ack_i); - - // //////////////////////////////////////////// - // Control Path, Wishbone bus bridge (wb master) - reg [1:0] we_del, oe_del; - - // Synchronize the async control signals - always @(posedge wb_clk) - if (wb_rst) begin - we_del <= 2'b0; - oe_del <= 2'b0; - end - else begin - we_del <= { we_del[0], EM_WE }; - oe_del <= { oe_del[0], EM_OE }; - end - - wire writing = we_del == 2'b01; - wire reading = oe_del == 2'b01; - - always @(posedge wb_clk) - if(writing || reading) - wb_adr_o <= { EM_A, 1'b0 }; - - always @(posedge wb_clk) - if(writing) - begin - wb_dat_mosi <= EM_D_in; - wb_sel_o <= ~EM_NBE; - end - - reg [15:0] EM_D_hold; - - always @(posedge wb_clk) - if(wb_ack_i) - EM_D_hold <= wb_dat_miso; - - assign EM_D_out = wb_ack_i ? wb_dat_miso : EM_D_hold; - - assign wb_cyc_o = wb_stb_o; - - always @(posedge wb_clk) - if(writing) - wb_we_o <= 1; - else if(wb_ack_i) // Turn off we when done. Could also use we_del[0], others... - wb_we_o <= 0; - - always @(posedge wb_clk) - if(writing || reading) - wb_stb_o <= 1; - else if(wb_ack_i) - wb_stb_o <= 0; - -endmodule // gpmc_wb diff --git a/fpga/usrp2/models/DCM_SP.v b/fpga/usrp2/models/DCM_SP.v new file mode 100644 index 000000000..2142fa736 --- /dev/null +++ b/fpga/usrp2/models/DCM_SP.v @@ -0,0 +1,1244 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/unisims/DCM_SP.v,v 1.9.4.3 2007/04/11 20:30:19 yanx Exp $ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995/2004 Xilinx, Inc. +// All Right Reserved. +/////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor : Xilinx +// \ \ \/ Version : 9.2i (J.36) +// \ \ Description : Xilinx Function Simulation Library Component +// / / Digital Clock Manager +// /___/ /\ Filename : DCM_SP.v +// \ \ / \ Timestamp : +// \___\/\___\ +// +// Revision: +// 02/28/06 - Initial version. +// 05/09/06 - Add clkin_ps_mkup and clkin_ps_mkup_win for phase shifting (CR 229789). +// 06/14/06 - Add clkin_ps_mkup_flag for multiple cycle delays (CR233283). +// 07/21/06 - Change range of variable phase shifting to +/- integer of 20*(Period-3ns). +// Give warning not support initial phase shifting for variable phase shifting. +// (CR 235216). +// 09/22/06 - Add lock_period and lock_fb to clkfb_div block (CR 418722). +// 12/19/06 - Add clkfb_div_en for clkfb2x divider (CR431210). +// 04/06/07 - Enable the clock out in clock low time after reset in model +// clock_divide_by_2 (CR 437471). +// End Revision + + +`timescale 1 ps / 1 ps + +module DCM_SP ( + CLK0, CLK180, CLK270, CLK2X, CLK2X180, CLK90, + CLKDV, CLKFX, CLKFX180, LOCKED, PSDONE, STATUS, + CLKFB, CLKIN, DSSEN, PSCLK, PSEN, PSINCDEC, RST); + +parameter CLKDV_DIVIDE = 2.0; +parameter integer CLKFX_DIVIDE = 1; +parameter integer CLKFX_MULTIPLY = 4; +parameter CLKIN_DIVIDE_BY_2 = "FALSE"; +parameter CLKIN_PERIOD = 10.0; // non-simulatable +parameter CLKOUT_PHASE_SHIFT = "NONE"; +parameter CLK_FEEDBACK = "1X"; +parameter DESKEW_ADJUST = "SYSTEM_SYNCHRONOUS"; // non-simulatable +parameter DFS_FREQUENCY_MODE = "LOW"; +parameter DLL_FREQUENCY_MODE = "LOW"; +parameter DSS_MODE = "NONE"; // non-simulatable +parameter DUTY_CYCLE_CORRECTION = "TRUE"; +parameter FACTORY_JF = 16'hC080; // non-simulatable +parameter integer MAXPERCLKIN = 1000000; // non-modifiable simulation parameter +parameter integer MAXPERPSCLK = 100000000; // non-modifiable simulation parameter +parameter integer PHASE_SHIFT = 0; +parameter integer SIM_CLKIN_CYCLE_JITTER = 300; // non-modifiable simulation parameter +parameter integer SIM_CLKIN_PERIOD_JITTER = 1000; // non-modifiable simulation parameter +parameter STARTUP_WAIT = "FALSE"; // non-simulatable + + +localparam PS_STEP = 25; + +input CLKFB, CLKIN, DSSEN; +input PSCLK, PSEN, PSINCDEC, RST; + +output CLK0, CLK180, CLK270, CLK2X, CLK2X180, CLK90; +output CLKDV, CLKFX, CLKFX180, LOCKED, PSDONE; +output [7:0] STATUS; + +reg CLK0, CLK180, CLK270, CLK2X, CLK2X180, CLK90; +reg CLKDV, CLKFX, CLKFX180; + +wire locked_out_out; +wire clkfb_in, clkin_in, dssen_in; +wire psclk_in, psen_in, psincdec_in, rst_in; +reg clk0_out; +reg clk2x_out, clkdv_out; +reg clkfx_out, clkfx180_en; +reg rst_flag; +reg locked_out, psdone_out, ps_overflow_out, ps_lock; +reg clkfb_div, clkfb_chk, clkfb_div_en; +integer clkdv_cnt; + +reg [1:0] clkfb_type; +reg [8:0] divide_type; +reg clkin_type; +reg [1:0] ps_type; +reg [3:0] deskew_adjust_mode; +reg dfs_mode_type; +reg dll_mode_type; +reg clk1x_type; +integer ps_in; + +reg lock_period, lock_delay, lock_clkin, lock_clkfb; +reg first_time_locked; +reg en_status; +reg ps_overflow_out_ext; +reg clkin_lost_out_ext; +reg clkfx_lost_out_ext; +reg [1:0] lock_out; +reg lock_out1_neg; +reg lock_fb, lock_ps, lock_ps_dly, lock_fb_dly, lock_fb_dly_tmp; +reg fb_delay_found; +reg clock_stopped; +reg clkin_chkin, clkfb_chkin; + +wire chk_enable, chk_rst; +wire clkin_div; +wire lock_period_pulse; +wire lock_period_dly, lock_period_dly1; + +reg clkin_ps, clkin_ps_tmp, clkin_ps_mkup, clkin_ps_mkup_win, clkin_ps_mkup_flag; +reg clkin_fb; + +time FINE_SHIFT_RANGE; +//time ps_delay, ps_delay_init, ps_delay_md, ps_delay_all, ps_max_range; +integer ps_delay, ps_delay_init, ps_delay_md, ps_delay_all, ps_max_range; +integer ps_delay_last; +integer ps_acc; +time clkin_edge; +time clkin_div_edge; +time clkin_ps_edge; +time delay_edge; +time clkin_period [2:0]; +time period; +integer period_int, period_int2, period_int3, period_ps_tmp; +time period_div; +integer period_orig_int; +time period_orig; +time period_ps; +time clkout_delay; +time fb_delay; +time period_fx, remain_fx; +time period_dv_high, period_dv_low; +time cycle_jitter, period_jitter; + +reg clkin_window, clkfb_window; +reg [2:0] rst_reg; +reg [12:0] numerator, denominator, gcd; +reg [23:0] i, n, d, p; + +reg notifier; + +initial begin + #1; + if ($realtime == 0) begin + $display ("Simulator Resolution Error : Simulator resolution is set to a value greater than 1 ps."); + $display ("In order to simulate the DCM_SP, the simulator resolution must be set to 1ps or smaller."); + $finish; + end +end + +initial begin + case (2.0) + 1.5 : divide_type = 'd3; + 2.0 : divide_type = 'd4; + 2.5 : divide_type = 'd5; + 3.0 : divide_type = 'd6; + 3.5 : divide_type = 'd7; + 4.0 : divide_type = 'd8; + 4.5 : divide_type = 'd9; + 5.0 : divide_type = 'd10; + 5.5 : divide_type = 'd11; + 6.0 : divide_type = 'd12; + 6.5 : divide_type = 'd13; + 7.0 : divide_type = 'd14; + 7.5 : divide_type = 'd15; + 8.0 : divide_type = 'd16; + 9.0 : divide_type = 'd18; + 10.0 : divide_type = 'd20; + 11.0 : divide_type = 'd22; + 12.0 : divide_type = 'd24; + 13.0 : divide_type = 'd26; + 14.0 : divide_type = 'd28; + 15.0 : divide_type = 'd30; + 16.0 : divide_type = 'd32; + default : begin + $display("Attribute Syntax Error : The attribute CLKDV_DIVIDE on DCM_SP instance %m is set to %0.1f. Legal values for this attribute are 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, or 16.0.", CLKDV_DIVIDE); + $finish; + end + endcase + + if ((CLKFX_DIVIDE <= 0) || (32 < CLKFX_DIVIDE)) begin + $display("Attribute Syntax Error : The attribute CLKFX_DIVIDE on DCM_SP instance %m is set to %d. Legal values for this attribute are 1 ... 32.", CLKFX_DIVIDE); + $finish; + end + + if ((CLKFX_MULTIPLY <= 1) || (32 < CLKFX_MULTIPLY)) begin + $display("Attribute Syntax Error : The attribute CLKFX_MULTIPLY on DCM_SP instance %m is set to %d. Legal values for this attribute are 2 ... 32.", CLKFX_MULTIPLY); + $finish; + end + + case (CLKIN_DIVIDE_BY_2) + "false" : clkin_type = 0; + "FALSE" : clkin_type = 0; + "true" : clkin_type = 1; + "TRUE" : clkin_type = 1; + default : begin + $display("Attribute Syntax Error : The attribute CLKIN_DIVIDE_BY_2 on DCM_SP instance %m is set to %s. Legal values for this attribute are TRUE or FALSE.", CLKIN_DIVIDE_BY_2); + $finish; + end + endcase + + case (CLKOUT_PHASE_SHIFT) + "NONE" : begin + ps_in = 256; + ps_type = 0; + end + "none" : begin + ps_in = 256; + ps_type = 0; + end + "FIXED" : begin + ps_in = PHASE_SHIFT + 256; + ps_type = 1; + end + "fixed" : begin + ps_in = PHASE_SHIFT + 256; + ps_type = 1; + end + "VARIABLE" : begin + ps_in = PHASE_SHIFT + 256; + ps_type = 2; + end + "variable" : begin + ps_in = PHASE_SHIFT + 256; + ps_type = 2; + if (PHASE_SHIFT != 0) + $display("Attribute Syntax Warning : The attribute PHASE_SHIFT on DCM_SP instance %m is set to %d. The maximum variable phase shift range is only valid when initial phase shift PHASE_SHIFT is zero.", PHASE_SHIFT); + end + default : begin + $display("Attribute Syntax Error : The attribute CLKOUT_PHASE_SHIFT on DCM_SP instance %m is set to %s. Legal values for this attribute are NONE, FIXED or VARIABLE.", CLKOUT_PHASE_SHIFT); + $finish; + end + endcase + + + case (CLK_FEEDBACK) + "none" : clkfb_type = 2'b00; + "NONE" : clkfb_type = 2'b00; + "1x" : clkfb_type = 2'b01; + "1X" : clkfb_type = 2'b01; + "2x" : clkfb_type = 2'b10; + "2X" : clkfb_type = 2'b10; + default : begin + $display("Attribute Syntax Error : The attribute CLK_FEEDBACK on DCM_SP instance %m is set to %s. Legal values for this attribute are NONE, 1X or 2X.", CLK_FEEDBACK); + $finish; + end + endcase + + case (DESKEW_ADJUST) + "source_synchronous" : deskew_adjust_mode = 8; + "SOURCE_SYNCHRONOUS" : deskew_adjust_mode = 8; + "system_synchronous" : deskew_adjust_mode = 11; + "SYSTEM_SYNCHRONOUS" : deskew_adjust_mode = 11; + "0" : deskew_adjust_mode = 0; + "1" : deskew_adjust_mode = 1; + "2" : deskew_adjust_mode = 2; + "3" : deskew_adjust_mode = 3; + "4" : deskew_adjust_mode = 4; + "5" : deskew_adjust_mode = 5; + "6" : deskew_adjust_mode = 6; + "7" : deskew_adjust_mode = 7; + "8" : deskew_adjust_mode = 8; + "9" : deskew_adjust_mode = 9; + "10" : deskew_adjust_mode = 10; + "11" : deskew_adjust_mode = 11; + "12" : deskew_adjust_mode = 12; + "13" : deskew_adjust_mode = 13; + "14" : deskew_adjust_mode = 14; + "15" : deskew_adjust_mode = 15; + default : begin + $display("Attribute Syntax Error : The attribute DESKEW_ADJUST on DCM_SP instance %m is set to %s. Legal values for this attribute are SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or 0 ... 15.", DESKEW_ADJUST); + $finish; + end + endcase + + case (DFS_FREQUENCY_MODE) + "high" : dfs_mode_type = 1; + "HIGH" : dfs_mode_type = 1; + "low" : dfs_mode_type = 0; + "LOW" : dfs_mode_type = 0; + default : begin + $display("Attribute Syntax Error : The attribute DFS_FREQUENCY_MODE on DCM_SP instance %m is set to %s. Legal values for this attribute are HIGH or LOW.", DFS_FREQUENCY_MODE); + $finish; + end + endcase + + period_jitter = SIM_CLKIN_PERIOD_JITTER; + cycle_jitter = SIM_CLKIN_CYCLE_JITTER; + + case (DLL_FREQUENCY_MODE) + "high" : dll_mode_type = 1; + "HIGH" : dll_mode_type = 1; + "low" : dll_mode_type = 0; + "LOW" : dll_mode_type = 0; + default : begin + $display("Attribute Syntax Error : The attribute DLL_FREQUENCY_MODE on DCM_SP instance %m is set to %s. Legal values for this attribute are HIGH or LOW.", DLL_FREQUENCY_MODE); + $finish; + end + endcase + + if ((dll_mode_type ==1) && (clkfb_type == 2'b10)) begin + $display("Attribute Syntax Error : The attributes DLL_FREQUENCY_MODE on DCM_SP instance %m is set to %s and CLK_FEEDBACK is set to %s. CLK_FEEDBACK 2X is not supported when DLL_FREQUENCY_MODE is HIGH.", DLL_FREQUENCY_MODE, CLK_FEEDBACK); + $finish; + end + + case (DSS_MODE) + "none" : ; + "NONE" : ; + default : begin + $display("Attribute Syntax Error : The attribute DSS_MODE on DCM_SP instance %m is set to %s. Legal values for this attribute is NONE.", DSS_MODE); + $finish; + end + endcase + + case (DUTY_CYCLE_CORRECTION) + "false" : clk1x_type = 0; + "FALSE" : clk1x_type = 0; + "true" : clk1x_type = 1; + "TRUE" : clk1x_type = 1; + default : begin + $display("Attribute Syntax Error : The attribute DUTY_CYCLE_CORRECTION on DCM_SP instance %m is set to %s. Legal values for this attribute are TRUE or FALSE.", DUTY_CYCLE_CORRECTION); + $finish; + end + endcase + + if ((PHASE_SHIFT < -255) || (PHASE_SHIFT > 255)) begin + $display("Attribute Syntax Error : The attribute PHASE_SHIFT on DCM_SP instance %m is set to %d. Legal values for this attribute are -255 ... 255.", PHASE_SHIFT); + $display("Error : PHASE_SHIFT = %d is not -255 ... 255.", PHASE_SHIFT); + $finish; + end + + case (STARTUP_WAIT) + "false" : ; + "FALSE" : ; + "true" : ; + "TRUE" : ; + default : begin + $display("Attribute Syntax Error : The attribute STARTUP_WAIT on DCM_SP instance %m is set to %s. Legal values for this attribute are TRUE or FALSE.", STARTUP_WAIT); + $finish; + end + endcase +end + +// +// fx parameters +// + +initial begin + gcd = 1; + for (i = 2; i <= CLKFX_MULTIPLY; i = i + 1) begin + if (((CLKFX_MULTIPLY % i) == 0) && ((CLKFX_DIVIDE % i) == 0)) + gcd = i; + end + numerator = CLKFX_MULTIPLY / gcd; + denominator = CLKFX_DIVIDE / gcd; +end + +// +// input wire delays +// + +buf b_clkin (clkin_in, CLKIN); +buf b_clkfb (clkfb_in, CLKFB); +buf b_dssen (dssen_in, DSSEN); +buf b_psclk (psclk_in, PSCLK); +buf b_psen (psen_in, PSEN); +buf b_psincdec (psincdec_in, PSINCDEC); +buf b_rst (rst_in, RST); +buf #100 b_LOCKED (LOCKED, locked_out_out); +buf #100 b_PSDONE (PSDONE, psdone_out); +buf b_ps_overflow (STATUS[0], ps_overflow_out_ext); +buf b_clkin_lost (STATUS[1], clkin_lost_out_ext); +buf b_clkfx_lost (STATUS[2], clkfx_lost_out_ext); + +assign STATUS[7:3] = 5'b0; + +dcm_sp_clock_divide_by_2 i_clock_divide_by_2 (clkin_in, clkin_type, clkin_div, rst_in); + +dcm_sp_maximum_period_check #("CLKIN", MAXPERCLKIN) i_max_clkin (clkin_in, rst_in); +dcm_sp_maximum_period_check #("PSCLK", MAXPERPSCLK) i_max_psclk (psclk_in, rst_in); + +dcm_sp_clock_lost i_clkin_lost (clkin_in, first_time_locked, clkin_lost_out, rst_in); +dcm_sp_clock_lost i_clkfx_lost (CLKFX, first_time_locked, clkfx_lost_out, rst_in); + +always @(rst_in or en_status or clkfx_lost_out or clkin_lost_out or ps_overflow_out) + if (rst_in == 1 || en_status == 0) begin + ps_overflow_out_ext = 0; + clkin_lost_out_ext = 0; + clkfx_lost_out_ext = 0; + end + else + begin + ps_overflow_out_ext = ps_overflow_out; + clkin_lost_out_ext = clkin_lost_out; + clkfx_lost_out_ext = clkfx_lost_out; + end + +always @(posedge rst_in or posedge LOCKED) + if (rst_in == 1) + en_status <= 0; + else + en_status <= 1; + + +always @(clkin_div) + clkin_ps_tmp <= #(ps_delay_md) clkin_div; + +always @(clkin_ps_tmp or clkin_ps_mkup or clkin_ps_mkup_win) + if (clkin_ps_mkup_win) + clkin_ps = clkin_ps_mkup; + else + clkin_ps = clkin_ps_tmp; + +always @(ps_delay_last or period_int or ps_delay) begin + period_int2 = 2 * period_int; + period_int3 = 3 * period_int; + if ((ps_delay_last >= period_int && ps_delay < period_int) || + (ps_delay_last >= period_int2 && ps_delay < period_int2) || + (ps_delay_last >= period_int3 && ps_delay < period_int3)) + clkin_ps_mkup_flag = 1; + else + clkin_ps_mkup_flag = 0; +end + +always @(posedge clkin_div or negedge clkin_div) begin + if (ps_type == 2'b10) begin + if ((ps_delay_last > 0 && ps_delay <= 0 ) || clkin_ps_mkup_flag == 1) begin + if (clkin_div) begin + clkin_ps_mkup_win <= 1; + clkin_ps_mkup <= 1; + #1; + @(negedge clkin_div) begin + clkin_ps_mkup_win <= 1; + clkin_ps_mkup <= 0; + end + end + else begin + clkin_ps_mkup_win <= 0; + clkin_ps_mkup <= 0; + #1; + @(posedge clkin_div) begin + clkin_ps_mkup_win <= 1; + clkin_ps_mkup <= 1; + end + @(negedge clkin_div) begin + clkin_ps_mkup_win <= 1; + clkin_ps_mkup <= 0; + end + end + end + else begin + clkin_ps_mkup_win <= 0; + clkin_ps_mkup <= 0; + end + ps_delay_last <= ps_delay; + end +end + +always @(clkin_ps or lock_fb) + clkin_fb = clkin_ps & lock_fb; + +always @(negedge clkfb_in or posedge rst_in) + if (rst_in) + clkfb_div_en <= 0; + else + if (lock_fb_dly && lock_period && lock_fb && ~clkin_ps) + clkfb_div_en <= 1; + +always @(posedge clkfb_in or posedge rst_in) + if (rst_in) + clkfb_div <= 0; + else + if (clkfb_div_en ) + clkfb_div <= ~clkfb_div; + +always @(clkfb_in or clkfb_div ) + if (clkfb_type == 2'b10 ) + clkfb_chk = clkfb_div; + else + clkfb_chk = clkfb_in & lock_fb_dly; + +always @(posedge clkin_fb or posedge chk_rst) + if (chk_rst) + clkin_chkin <= 0; + else + clkin_chkin <= 1; + +always @(posedge clkfb_chk or posedge chk_rst) + if (chk_rst) + clkfb_chkin <= 0; + else + clkfb_chkin <= 1; + + assign chk_rst = (rst_in==1 || clock_stopped==1 ) ? 1 : 0; + assign chk_enable = (clkin_chkin == 1 && clkfb_chkin == 1 && + lock_ps ==1 && lock_fb ==1 && lock_fb_dly == 1) ? 1 : 0; + +always @(posedge clkin_div or posedge rst_in) + if (rst_in) begin + period_div <= 0; + clkin_div_edge <= 0; + end + else + if ( clkin_div ==1 ) begin + clkin_div_edge <= $time; + if (($time - clkin_div_edge) <= (1.5 * period_div)) + period_div <= $time - clkin_div_edge; + else if ((period_div == 0) && (clkin_div_edge != 0)) + period_div <= $time - clkin_div_edge; + end + +always @(posedge clkin_ps or posedge rst_in) + if (rst_in) begin + period_ps <= 0; + clkin_ps_edge <= 0; + end + else + if (clkin_ps == 1 ) begin + clkin_ps_edge <= $time; + if (($time - clkin_ps_edge) <= (1.5 * period_ps)) + period_ps <= $time - clkin_ps_edge; + else if ((period_ps == 0) && (clkin_ps_edge != 0)) + period_ps <= $time - clkin_ps_edge; + end + +always @(posedge clkin_ps) begin + lock_ps <= lock_period; + lock_ps_dly <= lock_ps; + lock_fb <= lock_ps_dly; + lock_fb_dly_tmp <= lock_fb; +end + +always @(negedge clkin_ps or posedge rst_in) + if (rst_in) + lock_fb_dly <= 1'b0; + else + lock_fb_dly <= #(period/4) lock_fb_dly_tmp; + + +always @(period or fb_delay ) + if (fb_delay == 0) + clkout_delay = 0; + else + clkout_delay = period - fb_delay; + +// +// generate master reset signal +// + +always @(posedge clkin_in) begin + rst_reg[0] <= rst_in; + rst_reg[1] <= rst_reg[0] & rst_in; + rst_reg[2] <= rst_reg[1] & rst_reg[0] & rst_in; +end + +reg rst_tmp1, rst_tmp2; +initial +begin +rst_tmp1 = 0; +rst_tmp2 = 0; +rst_flag = 0; +end + +always @(rst_in) +begin + if (rst_in) + rst_flag = 0; + + rst_tmp1 = rst_in; + if (rst_tmp1 == 0 && rst_tmp2 == 1) begin + if ((rst_reg[2] & rst_reg[1] & rst_reg[0]) == 0) begin + rst_flag = 1; + $display("Input Error : RST on instance %m must be asserted for 3 CLKIN clock cycles."); + end + end + rst_tmp2 = rst_tmp1; +end + +initial begin + CLK0 = 0; + CLK180 = 0; + CLK270 = 0; + CLK2X = 0; + CLK2X180 = 0; + CLK90 = 0; + CLKDV = 0; + CLKFX = 0; + CLKFX180 = 0; + clk0_out = 0; + clk2x_out = 0; + clkdv_out = 0; + clkdv_cnt = 0; + clkfb_window = 0; + clkfx_out = 0; + clkfx180_en = 0; + clkin_div_edge = 0; + clkin_period[0] = 0; + clkin_period[1] = 0; + clkin_period[2] = 0; + clkin_edge = 0; + clkin_ps_edge = 0; + clkin_window = 0; + clkout_delay = 0; + clock_stopped = 1; + fb_delay = 0; + fb_delay_found = 0; + lock_clkfb = 0; + lock_clkin = 0; + lock_delay = 0; + lock_fb = 0; + lock_fb_dly = 0; + lock_out = 2'b00; + lock_out1_neg = 0; + lock_period = 0; + lock_ps = 0; + lock_ps_dly = 0; + locked_out = 0; + period = 0; + period_int = 0; + period_int2 = 0; + period_int3 = 0; + period_div = 0; + period_fx = 0; + period_orig = 0; + period_orig_int = 0; + period_ps = 0; + psdone_out = 0; + ps_delay = 0; + ps_delay_md = 0; + ps_delay_init = 0; + ps_acc = 0; + ps_delay_all = 0; + ps_lock = 0; + ps_overflow_out = 0; + ps_overflow_out_ext = 0; + clkin_lost_out_ext = 0; + clkfx_lost_out_ext = 0; + rst_reg = 3'b000; + first_time_locked = 0; + en_status = 0; + clkfb_div = 0; + clkin_chkin = 0; + clkfb_chkin = 0; + clkin_ps_mkup = 0; + clkin_ps_mkup_win = 0; + clkin_ps_mkup_flag = 0; + ps_delay_last = 0; + clkin_ps_tmp = 0; +end + +// RST less than 3 cycles, lock = x + + assign locked_out_out = (rst_flag) ? 1'bx : locked_out; + +// +// detect_first_time_locked +// +always @(posedge locked_out) + if (first_time_locked == 0) + first_time_locked <= 1; + +// +// phase shift parameters +// + +always @(posedge lock_period) + ps_delay_init <= ps_in * period_orig /256; + + +always @(period) begin + period_int = period; + if (clkin_type==1) + period_ps_tmp = 2 * period; + else + period_ps_tmp = period; + + if (period_ps_tmp > 3000) + ps_max_range = 20 * (period_ps_tmp - 3000)/1000; + else + ps_max_range = 0; +end + +always @(ps_delay or rst_in or period_int or lock_period) + if ( rst_in) + ps_delay_md = 0; + else if (lock_period) begin + ps_delay_md = period_int + ps_delay % period_int; + end + +always @(posedge psclk_in or posedge rst_in or posedge lock_period_pulse) + if (rst_in) begin + ps_delay <= 0; + ps_overflow_out <= 0; + ps_acc <= 0; + end + else if (lock_period_pulse) + ps_delay <= ps_delay_init; + else + if (ps_type == 2'b10) + if (psen_in) begin + if (ps_lock == 1) + $display(" Warning : Please wait for PSDONE signal before adjusting the Phase Shift."); + else if (lock_ps) begin + if (psincdec_in == 1) begin + if (ps_acc > ps_max_range) + ps_overflow_out <= 1; + else begin + ps_delay <= ps_delay + PS_STEP; + ps_acc <= ps_acc + 1; + ps_overflow_out <= 0; + end + ps_lock <= 1; + end + else if (psincdec_in == 0) begin + if (ps_acc < -ps_max_range) + ps_overflow_out <= 1; + else begin + ps_delay <= ps_delay - PS_STEP; + ps_acc <= ps_acc - 1; + ps_overflow_out <= 0; + end + ps_lock <= 1; + end + end + end + +always @(posedge ps_lock) begin + @(posedge clkin_ps) + @(posedge psclk_in) + @(posedge psclk_in) + @(posedge psclk_in) + psdone_out <= 1; + @(posedge psclk_in) + psdone_out <= 0; + ps_lock <= 0; +end + +// +// determine clock period +// + +always @(posedge clkin_div or negedge clkin_div or posedge rst_in) + if (rst_in == 1) begin + clkin_period[0] <= 0; + clkin_period[1] <= 0; + clkin_period[2] <= 0; + clkin_edge <= 0; + end + else + if (clkin_div == 1) begin + clkin_edge <= $time; + clkin_period[2] <= clkin_period[1]; + clkin_period[1] <= clkin_period[0]; + if (clkin_edge != 0) + clkin_period[0] <= $time - clkin_edge; + end + else if (clkin_div == 0) + if (lock_period == 1) + if (100000000 < clkin_period[0]/1000) + begin + end + else if ((period_orig * 2 < clkin_period[0]) && (clock_stopped == 0)) begin + clkin_period[0] <= clkin_period[1]; + end + +always @(negedge clkin_div or posedge rst_in) + if (rst_in == 1) begin + lock_period <= 0; + clock_stopped <= 1; + end + else begin + if (lock_period == 1'b0) begin + if ((clkin_period[0] != 0) && + (clkin_period[0] - cycle_jitter <= clkin_period[1]) && + (clkin_period[1] <= clkin_period[0] + cycle_jitter) && + (clkin_period[1] - cycle_jitter <= clkin_period[2]) && + (clkin_period[2] <= clkin_period[1] + cycle_jitter)) begin + lock_period <= 1; + period_orig <= (clkin_period[0] + + clkin_period[1] + + clkin_period[2]) / 3; + period <= clkin_period[0]; + end + end + else if (lock_period == 1'b1) begin + if (100000000 < (clkin_period[0] / 1000)) begin + $display(" Warning : CLKIN stopped toggling on instance %m exceeds %d ms. Current CLKIN Period = %1.3f ns.", 100, clkin_period[0] / 1000.0); + lock_period <= 0; + @(negedge rst_reg[2]); + end + else if ((period_orig * 2 < clkin_period[0]) && clock_stopped == 1'b0) begin + clock_stopped <= 1'b1; + end + else if ((clkin_period[0] < period_orig - period_jitter) || + (period_orig + period_jitter < clkin_period[0])) begin + $display(" Warning : Input Clock Period Jitter on instance %m exceeds %1.3f ns. Locked CLKIN Period = %1.3f. Current CLKIN Period = %1.3f.", period_jitter / 1000.0, period_orig / 1000.0, clkin_period[0] / 1000.0); + lock_period <= 0; + @(negedge rst_reg[2]); + end + else if ((clkin_period[0] < clkin_period[1] - cycle_jitter) || + (clkin_period[1] + cycle_jitter < clkin_period[0])) begin + $display(" Warning : Input Clock Cycle-Cycle Jitter on instance %m exceeds %1.3f ns. Previous CLKIN Period = %1.3f. Current CLKIN Period = %1.3f.", cycle_jitter / 1000.0, clkin_period[1] / 1000.0, clkin_period[0] / 1000.0); + lock_period <= 0; + @(negedge rst_reg[2]); + end + else begin + period <= clkin_period[0]; + clock_stopped <= 1'b0; + end + end +end + + assign #1 lock_period_dly1 = lock_period; + assign #(period/2) lock_period_dly = lock_period_dly1; + assign lock_period_pulse = (lock_period_dly1==1 && lock_period_dly==0) ? 1 : 0; + +// +// determine clock delay +// + +//always @(posedge lock_period or posedge rst_in) +always @(posedge lock_ps_dly or posedge rst_in) + if (rst_in) begin + fb_delay <= 0; + fb_delay_found <= 0; + end + else begin + if (lock_period && clkfb_type != 2'b00) begin + if (clkfb_type == 2'b01) begin + @(posedge CLK0 or rst_in) + delay_edge = $time; + end + else if (clkfb_type == 2'b10) begin + @(posedge CLK2X or rst_in) + delay_edge = $time; + end + @(posedge clkfb_in or rst_in) begin + fb_delay <= ($time - delay_edge) % period_orig; + fb_delay_found <= 1; + end + end + end + +// +// determine feedback lock +// + +always @(posedge clkfb_chk or posedge rst_in) + if (rst_in) + clkfb_window <= 0; + else begin + clkfb_window <= 1; + #cycle_jitter clkfb_window <= 0; + end + +always @(posedge clkin_fb or posedge rst_in) + if (rst_in) + clkin_window <= 0; + else begin + clkin_window <= 1; + #cycle_jitter clkin_window <= 0; + end + +always @(posedge clkin_fb or posedge rst_in) + if (rst_in) + lock_clkin <= 0; + else begin + #1 + if ((clkfb_window && fb_delay_found) || (clkin_lost_out == 1'b1 && lock_out[0]==1'b1)) + lock_clkin <= 1; + else + if (chk_enable==1) + lock_clkin <= 0; + end + +always @(posedge clkfb_chk or posedge rst_in) + if (rst_in) + lock_clkfb <= 0; + else begin + #1 + if ((clkin_window && fb_delay_found) || (clkin_lost_out == 1'b1 && lock_out[0]==1'b1)) + lock_clkfb <= 1; + else + if (chk_enable ==1) + lock_clkfb <= 0; + end + +always @(negedge clkin_fb or posedge rst_in) + if (rst_in) + lock_delay <= 0; + else + lock_delay <= lock_clkin || lock_clkfb; + +// +// generate lock signal +// + +always @(posedge clkin_ps or posedge rst_in) + if (rst_in) begin + lock_out <= 2'b0; + locked_out <=0; + end + else begin + if (clkfb_type == 2'b00) + lock_out[0] <= lock_period; + else + lock_out[0] <= lock_period & lock_delay & lock_fb; + lock_out[1] <= lock_out[0]; + locked_out <= lock_out[1]; + end + +always @(negedge clkin_ps or posedge rst_in) + if (rst_in) + lock_out1_neg <= 0; + else + lock_out1_neg <= lock_out[1]; + + +// +// generate the clk1x_out +// + +always @(posedge clkin_ps or negedge clkin_ps or posedge rst_in) + if (rst_in) + clk0_out <= 0; + else + if (clkin_ps ==1) + if (clk1x_type==1 && lock_out[0]) begin + clk0_out <= 1; + #(period / 2) + clk0_out <= 0; + end + else + clk0_out <= 1; + else + if (clkin_ps == 0 && ((clk1x_type && lock_out[0]) == 0 || (lock_out[0]== 1 && lock_out[1]== 0))) + clk0_out <= 0; + +// +// generate the clk2x_out +// + +always @(posedge clkin_ps or posedge rst_in) + if (rst_in) + clk2x_out <= 0; + else begin + clk2x_out <= 1; + #(period / 4) + clk2x_out <= 0; + #(period / 4) + clk2x_out <= 1; + #(period / 4) + clk2x_out <= 0; + end + +// +// generate the clkdv_out +// + +always @(posedge clkin_ps or negedge clkin_ps or posedge rst_in) + if (rst_in) begin + clkdv_out <= 1'b0; + clkdv_cnt <= 0; + end + else + if (lock_out1_neg) begin + if (clkdv_cnt >= divide_type -1) + clkdv_cnt <= 0; + else + clkdv_cnt <= clkdv_cnt + 1; + + if (clkdv_cnt < divide_type /2) + clkdv_out <= 1'b1; + else + if ( (divide_type[0] == 1'b1) && dll_mode_type == 1'b0) + clkdv_out <= #(period/4) 1'b0; + else + clkdv_out <= 1'b0; + end + + +// +// generate fx output signal +// + +always @(lock_period or period or denominator or numerator) begin + if (lock_period == 1'b1) begin + period_fx = (period * denominator) / (numerator * 2); + remain_fx = (period * denominator) % (numerator * 2); + end +end + +always @(posedge clkin_ps or posedge clkin_lost_out or posedge rst_in ) + if (rst_in == 1) + clkfx_out = 1'b0; + else if (clkin_lost_out == 1'b1 ) begin + if (locked_out == 1) + @(negedge rst_reg[2]); + end + else + if (lock_out[1] == 1) begin + clkfx_out = 1'b1; + for (p = 0; p < (numerator * 2 - 1); p = p + 1) begin + #(period_fx); + if (p < remain_fx) + #1; + clkfx_out = !clkfx_out; + end + if (period_fx > (period / 2)) begin + #(period_fx - (period / 2)); + end + end + +// +// generate all output signal +// + +always @(rst_in) +if (rst_in) begin + assign CLK0 = 0; + assign CLK90 = 0; + assign CLK180 = 0; + assign CLK270 = 0; + assign CLK2X = 0; + assign CLK2X180 =0; + assign CLKDV = 0; + assign CLKFX = 0; + assign CLKFX180 = 0; +end +else begin + deassign CLK0; + deassign CLK90; + deassign CLK180; + deassign CLK270; + deassign CLK2X; + deassign CLK2X180; + deassign CLKDV; + deassign CLKFX; + deassign CLKFX180; +end + +always @(clk0_out) begin + CLK0 <= #(clkout_delay) clk0_out && (clkfb_type != 2'b00); + CLK90 <= #(clkout_delay + period / 4) clk0_out && !dll_mode_type && (clkfb_type != 2'b00); + CLK180 <= #(clkout_delay) ~clk0_out && (clkfb_type != 2'b00); + CLK270 <= #(clkout_delay + period / 4) ~clk0_out && !dll_mode_type && (clkfb_type != 2'b00); + end + +always @(clk2x_out) begin + CLK2X <= #(clkout_delay) clk2x_out && !dll_mode_type && (clkfb_type != 2'b00); + CLK2X180 <= #(clkout_delay) ~clk2x_out && !dll_mode_type && (clkfb_type != 2'b00); +end + +always @(clkdv_out) + CLKDV <= #(clkout_delay) clkdv_out && (clkfb_type != 2'b00); + +always @(clkfx_out ) + CLKFX <= #(clkout_delay) clkfx_out; + +always @( clkfx_out or first_time_locked or locked_out) + if ( ~first_time_locked) + CLKFX180 = 0; + else + CLKFX180 <= #(clkout_delay) ~clkfx_out; + + +endmodule + +////////////////////////////////////////////////////// + +module dcm_sp_clock_divide_by_2 (clock, clock_type, clock_out, rst); +input clock; +input clock_type; +input rst; +output clock_out; + +reg clock_out; +reg clock_div2; +reg [2:0] rst_reg; +wire clk_src; + +initial begin + clock_out = 1'b0; + clock_div2 = 1'b0; +end + +always @(posedge clock) + clock_div2 <= ~clock_div2; + +always @(posedge clock) begin + rst_reg[0] <= rst; + rst_reg[1] <= rst_reg[0] & rst; + rst_reg[2] <= rst_reg[1] & rst_reg[0] & rst; +end + +assign clk_src = (clock_type) ? clock_div2 : clock; + +always @(clk_src or rst or rst_reg) + if (rst == 1'b0) + clock_out = clk_src; + else if (rst == 1'b1) begin + clock_out = 1'b0; + @(negedge rst_reg[2]); + if (clk_src == 1'b1) + @(negedge clk_src); + end + + +endmodule + +module dcm_sp_maximum_period_check (clock, rst); +parameter clock_name = ""; +parameter maximum_period = 0; +input clock; +input rst; + +time clock_edge; +time clock_period; + +initial begin + clock_edge = 0; + clock_period = 0; +end + +always @(posedge clock) begin + clock_edge <= $time; +// clock_period <= $time - clock_edge; + clock_period = $time - clock_edge; + if (clock_period > maximum_period ) begin + if (rst == 0) + $display(" Warning : Input clock period of %1.3f ns, on the %s port of instance %m exceeds allowed value of %1.3f ns at time %1.3f ns.", clock_period/1000.0, clock_name, maximum_period/1000.0, $time/1000.0); + end +end +endmodule + +module dcm_sp_clock_lost (clock, enable, lost, rst); +input clock; +input enable; +input rst; +output lost; + +time clock_edge; +reg [63:0] period; +reg clock_low, clock_high; +reg clock_posedge, clock_negedge; +reg lost_r, lost_f, lost; +reg clock_second_pos, clock_second_neg; + +initial begin + clock_edge = 0; + clock_high = 0; + clock_low = 0; + lost_r = 0; + lost_f = 0; + period = 0; + clock_posedge = 0; + clock_negedge = 0; + clock_second_pos = 0; + clock_second_neg = 0; +end + +always @(posedge clock or posedge rst) + if (rst==1) + period <= 0; + else begin + clock_edge <= $time; + if (period != 0 && (($time - clock_edge) <= (1.5 * period))) + period <= $time - clock_edge; + else if (period != 0 && (($time - clock_edge) > (1.5 * period))) + period <= 0; + else if ((period == 0) && (clock_edge != 0) && clock_second_pos == 1) + period <= $time - clock_edge; + end + + +always @(posedge clock or posedge rst) + if (rst) + lost_r <= 0; + else + if (enable == 1 && clock_second_pos == 1) begin + #1; + if ( period != 0) + lost_r <= 0; + #((period * 9.1) / 10) + if ((clock_low != 1'b1) && (clock_posedge != 1'b1) && rst == 0) + lost_r <= 1; + end + +always @(posedge clock or negedge clock or posedge rst) + if (rst) begin + clock_second_pos <= 0; + clock_second_neg <= 0; + end + else if (clock) + clock_second_pos <= 1; + else if (~clock) + clock_second_neg <= 1; + +always @(negedge clock or posedge rst) + if (rst==1) begin + lost_f <= 0; + end + else begin + if (enable == 1 && clock_second_neg == 1) begin + if ( period != 0) + lost_f <= 0; + #((period * 9.1) / 10) + if ((clock_high != 1'b1) && (clock_negedge != 1'b1) && rst == 0) + lost_f <= 1; + end + end + +always @( lost_r or lost_f or enable) +begin + if (enable == 1) + lost = lost_r | lost_f; + else + lost = 0; +end + + +always @(posedge clock or negedge clock or posedge rst) + if (rst==1) begin + clock_low <= 1'b0; + clock_high <= 1'b0; + clock_posedge <= 1'b0; + clock_negedge <= 1'b0; + end + else begin + if (clock ==1) begin + clock_low <= 1'b0; + clock_high <= 1'b1; + clock_posedge <= 1'b0; + clock_negedge <= 1'b1; + end + else if (clock == 0) begin + clock_low <= 1'b1; + clock_high <= 1'b0; + clock_posedge <= 1'b1; + clock_negedge <= 1'b0; + end +end + + +endmodule diff --git a/fpga/usrp2/models/IBUFG.v b/fpga/usrp2/models/IBUFG.v new file mode 100644 index 000000000..c21cc1dc8 --- /dev/null +++ b/fpga/usrp2/models/IBUFG.v @@ -0,0 +1,59 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/unisims/IBUFG.v,v 1.7 2007/05/23 21:43:34 patrickp Exp $ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995/2004 Xilinx, Inc. +// All Right Reserved. +/////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor : Xilinx +// \ \ \/ Version : 10.1 +// \ \ Description : Xilinx Functional Simulation Library Component +// / / Input Clock Buffer +// /___/ /\ Filename : IBUFG.v +// \ \ / \ Timestamp : Thu Mar 25 16:42:24 PST 2004 +// \___\/\___\ +// +// Revision: +// 03/23/04 - Initial version. +// 05/23/07 - Changed timescale to 1 ps / 1 ps. + +`timescale 1 ps / 1 ps + + +module IBUFG (O, I); + + parameter CAPACITANCE = "DONT_CARE"; + parameter IBUF_DELAY_VALUE = "0"; + parameter IOSTANDARD = "DEFAULT"; + + output O; + input I; + + buf B1 (O, I); + + initial begin + + case (CAPACITANCE) + + "LOW", "NORMAL", "DONT_CARE" : ; + default : begin + $display("Attribute Syntax Error : The attribute CAPACITANCE on IBUFG instance %m is set to %s. Legal values for this attribute are DONT_CARE, LOW or NORMAL.", CAPACITANCE); + $finish; + end + + endcase + + + case (IBUF_DELAY_VALUE) + + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16" : ; + default : begin + $display("Attribute Syntax Error : The attribute IBUF_DELAY_VALUE on IBUFG instance %m is set to %s. Legal values for this attribute are 0, 1, 2, ... or 16.", IBUF_DELAY_VALUE); + $finish; + end + + endcase + + end // initial begin + +endmodule diff --git a/fpga/usrp2/models/IBUFGDS.v b/fpga/usrp2/models/IBUFGDS.v new file mode 100644 index 000000000..01c108c8d --- /dev/null +++ b/fpga/usrp2/models/IBUFGDS.v @@ -0,0 +1,87 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/unisims/IBUFGDS.v,v 1.8 2007/07/26 23:22:55 fphillip Exp $ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995/2004 Xilinx, Inc. +// All Right Reserved. +/////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor : Xilinx +// \ \ \/ Version : 10.1 +// \ \ Description : Xilinx Functional Simulation Library Component +// / / Differential Signaling Input Clock Buffer +// /___/ /\ Filename : IBUFGDS.v +// \ \ / \ Timestamp : Thu Mar 25 16:42:24 PST 2004 +// \___\/\___\ +// +// Revision: +// 03/23/04 - Initial version. +// 05/23/07 - Changed timescale to 1 ps / 1 ps. +// 07/26/07 - Add else to handle x case for o_out (CR 424214). +// End Revision + + +`timescale 1 ps / 1 ps + + +module IBUFGDS (O, I, IB); + + parameter CAPACITANCE = "DONT_CARE"; + parameter DIFF_TERM = "FALSE"; + parameter IBUF_DELAY_VALUE = "0"; + parameter IOSTANDARD = "DEFAULT"; + + output O; + input I, IB; + + reg o_out; + + buf b_0 (O, o_out); + + initial begin + + case (CAPACITANCE) + + "LOW", "NORMAL", "DONT_CARE" : ; + default : begin + $display("Attribute Syntax Error : The attribute CAPACITANCE on IBUFGDS instance %m is set to %s. Legal values for this attribute are DONT_CARE, LOW or NORMAL.", CAPACITANCE); + $finish; + end + + endcase + + + case (DIFF_TERM) + + "TRUE", "FALSE" : ; + default : begin + $display("Attribute Syntax Error : The attribute DIFF_TERM on IBUFGDS instance %m is set to %s. Legal values for this attribute are TRUE or FALSE.", DIFF_TERM); + $finish; + end + + endcase + + + case (IBUF_DELAY_VALUE) + + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16" : ; + default : begin + $display("Attribute Syntax Error : The attribute IBUF_DELAY_VALUE on IBUFGDS instance %m is set to %s. Legal values for this attribute are 0, 1, 2, ... or 16.", IBUF_DELAY_VALUE); + $finish; + end + + endcase + + end + + always @(I or IB) begin + if (I == 1'b1 && IB == 1'b0) + o_out <= I; + else if (I == 1'b0 && IB == 1'b1) + o_out <= I; + else if (I == 1'bx && IB == 1'bx) + o_out <= 1'bx; + end + +endmodule + + diff --git a/fpga/usrp2/models/IDDR2.v b/fpga/usrp2/models/IDDR2.v new file mode 100644 index 000000000..727f1c568 --- /dev/null +++ b/fpga/usrp2/models/IDDR2.v @@ -0,0 +1,172 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/spartan4/IDDR2.v,v 1.1 2004/06/21 21:45:36 wloo Exp $ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995/2004 Xilinx, Inc. +// All Right Reserved. +/////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor : Xilinx +// \ \ \/ Version : 10.1 +// \ \ Description : Xilinx Functional Simulation Library Component +// / / Dual Data Rate Input D Flip-Flop +// /___/ /\ Filename : IDDR2.v +// \ \ / \ Timestamp : Thu Mar 25 16:43:51 PST 2004 +// \___\/\___\ +// +// Revision: +// 03/23/04 - Initial version. + +`timescale 1 ps / 1 ps + +module IDDR2 (Q0, Q1, C0, C1, CE, D, R, S); + + output Q0; + output Q1; + + input C0; + input C1; + input CE; + input D; + tri0 GSR = glbl.GSR; + input R; + input S; + + parameter DDR_ALIGNMENT = "NONE"; + parameter INIT_Q0 = 1'b0; + parameter INIT_Q1 = 1'b0; + parameter SRTYPE = "SYNC"; + + reg q0_out, q1_out; + reg q0_out_int, q1_out_int; + reg q0_c1_out_int, q1_c0_out_int; + + buf buf_q0 (Q0, q0_out); + buf buf_q1 (Q1, q1_out); + + + initial begin + + if ((INIT_Q0 != 1'b0) && (INIT_Q0 != 1'b1)) begin + $display("Attribute Syntax Error : The attribute INIT_Q0 on IDDR2 instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT_Q0); + $finish; + end + + if ((INIT_Q1 != 1'b0) && (INIT_Q1 != 1'b1)) begin + $display("Attribute Syntax Error : The attribute INIT_Q0 on IDDR2 instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT_Q1); + $finish; + end + + if ((DDR_ALIGNMENT != "C1") && (DDR_ALIGNMENT != "C0") && (DDR_ALIGNMENT != "NONE")) begin + $display("Attribute Syntax Error : The attribute DDR_ALIGNMENT on IDDR2 instance %m is set to %s. Legal values for this attribute are C0, C1 or NONE.", DDR_ALIGNMENT); + $finish; + end + + if ((SRTYPE != "ASYNC") && (SRTYPE != "SYNC")) begin + $display("Attribute Syntax Error : The attribute SRTYPE on IDDR2 instance %m is set to %s. Legal values for this attribute are ASYNC or SYNC.", SRTYPE); + $finish; + end + + end // initial begin + + + always @(GSR or R or S) begin + + if (GSR == 1) begin + + assign q0_out_int = INIT_Q0; + assign q1_out_int = INIT_Q1; + assign q0_c1_out_int = INIT_Q0; + assign q1_c0_out_int = INIT_Q1; + + end + else begin + + deassign q0_out_int; + deassign q1_out_int; + deassign q0_c1_out_int; + deassign q1_c0_out_int; + + if (SRTYPE == "ASYNC") begin + if (R == 1) begin + assign q0_out_int = 0; + assign q1_out_int = 0; + assign q0_c1_out_int = 0; + assign q1_c0_out_int = 0; + end + else if (R == 0 && S == 1) begin + assign q0_out_int = 1; + assign q1_out_int = 1; + assign q0_c1_out_int = 1; + assign q1_c0_out_int = 1; + end + end // if (SRTYPE == "ASYNC") + + end // if (GSR == 1'b0) + + end // always @ (GSR or R or S) + + + always @(posedge C0) begin + if (R == 1 && SRTYPE == "SYNC") begin + q0_out_int <= 0; + q1_c0_out_int <= 0; + end + else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin + q0_out_int <= 1; + q1_c0_out_int <= 1; + end + else if (CE == 1 && R == 0 && S == 0) begin + q0_out_int <= D; + q1_c0_out_int <= q1_out_int; + end + end // always @ (posedge C0) + + + always @(posedge C1) begin + if (R == 1 && SRTYPE == "SYNC") begin + q1_out_int <= 0; + q0_c1_out_int <= 0; + end + else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin + q1_out_int <= 1; + q0_c1_out_int <= 1; + end + else if (CE == 1 && R == 0 && S == 0) begin + q1_out_int <= D; + q0_c1_out_int <= q0_out_int; + end + end // always @ (posedge C1) + + + always @(q0_out_int or q1_out_int or q1_c0_out_int or q0_c1_out_int) begin + + case (DDR_ALIGNMENT) + "NONE" : begin + q0_out <= q0_out_int; + q1_out <= q1_out_int; + end + "C0" : begin + q0_out <= q0_out_int; + q1_out <= q1_c0_out_int; + end + "C1" : begin + q0_out <= q0_c1_out_int; + q1_out <= q1_out_int; + end + endcase // case(DDR_ALIGNMENT) + + end // always @ (q0_out_int or q1_out_int or q1_c0_out_int or q0_c1_out_int) + + + specify + + if (C0) (C0 => Q0) = (100, 100); + if (C0) (C0 => Q1) = (100, 100); + if (C1) (C1 => Q1) = (100, 100); + if (C1) (C1 => Q0) = (100, 100); + specparam PATHPULSE$ = 0; + + endspecify + +endmodule // IDDR2 + diff --git a/fpga/usrp2/models/ODDR2.v b/fpga/usrp2/models/ODDR2.v new file mode 100644 index 000000000..67e71761d --- /dev/null +++ b/fpga/usrp2/models/ODDR2.v @@ -0,0 +1,157 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/spartan4/ODDR2.v,v 1.1 2004/06/21 21:45:36 wloo Exp $ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995/2004 Xilinx, Inc. +// All Right Reserved. +/////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor : Xilinx +// \ \ \/ Version : 10.1 +// \ \ Description : Xilinx Functional Simulation Library Component +// / / Dual Data Rate Output D Flip-Flop +// /___/ /\ Filename : ODDR2.v +// \ \ / \ Timestamp : Thu Mar 25 16:43:52 PST 2004 +// \___\/\___\ +// +// Revision: +// 03/23/04 - Initial version. + +`timescale 1 ps / 1 ps + +module ODDR2 (Q, C0, C1, CE, D0, D1, R, S); + + output Q; + + input C0; + input C1; + input CE; + input D0; + input D1; + tri0 GSR = glbl.GSR; + input R; + input S; + + parameter DDR_ALIGNMENT = "NONE"; + parameter INIT = 1'b0; + parameter SRTYPE = "SYNC"; + + reg q_out, q_d0_c1_out_int, q_d1_c0_out_int; + + buf buf_q (Q, q_out); + + + initial begin + + if ((INIT != 1'b0) && (INIT != 1'b1)) begin + $display("Attribute Syntax Error : The attribute INIT on ODDR2 instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT); + $finish; + end + + if ((DDR_ALIGNMENT != "NONE") && (DDR_ALIGNMENT != "C0") && (DDR_ALIGNMENT != "C1")) begin + $display("Attribute Syntax Error : The attribute DDR_ALIGNMENT on ODDR2 instance %m is set to %s. Legal values for this attribute are NONE, C0 or C1.", DDR_ALIGNMENT); + $finish; + end + + if ((SRTYPE != "ASYNC") && (SRTYPE != "SYNC")) begin + $display("Attribute Syntax Error : The attribute SRTYPE on ODDR2 instance %m is set to %s. Legal values for this attribute are ASYNC or SYNC.", SRTYPE); + $finish; + end + + end // initial begin + + + always @(GSR or R or S) begin + + if (GSR == 1) begin + + assign q_out = INIT; + assign q_d0_c1_out_int = INIT; + assign q_d1_c0_out_int = INIT; + + end + else begin + + deassign q_out; + deassign q_d0_c1_out_int; + deassign q_d1_c0_out_int; + + if (SRTYPE == "ASYNC") begin + if (R == 1) begin + assign q_out = 0; + assign q_d0_c1_out_int = 0; + assign q_d1_c0_out_int = 0; + end + else if (R == 0 && S == 1) begin + assign q_out = 1; + assign q_d0_c1_out_int = 1; + assign q_d1_c0_out_int = 1; + end + end // if (SRTYPE == "ASYNC") + + end // if (GSR == 1'b0) + + end // always @ (GSR or R or S) + + + always @(posedge C0) begin + + if (R == 1 && SRTYPE == "SYNC") begin + q_out <= 0; + q_d1_c0_out_int <= 0; + end + else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin + q_out <= 1; + q_d1_c0_out_int <= 1; + end + else if (CE == 1 && R == 0 && S == 0) begin + + if (DDR_ALIGNMENT == "C1") + q_out <= q_d0_c1_out_int; + else begin + q_out <= D0; + + if (DDR_ALIGNMENT == "C0") + q_d1_c0_out_int <= D1; + end + + end // if (CE == 1 && R == 0 && S == 0) + + end // always @ (posedge C0) + + + always @(posedge C1) begin + + if (R == 1 && SRTYPE == "SYNC") begin + q_out <= 0; + q_d0_c1_out_int <= 0; + end + else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin + q_out <= 1; + q_d0_c1_out_int <= 1; + end + else if (CE == 1 && R == 0 && S == 0) begin + + if (DDR_ALIGNMENT == "C0") + q_out <= q_d1_c0_out_int; + else begin + q_out <= D1; + + if (DDR_ALIGNMENT == "C1") + q_d0_c1_out_int <= D0; + end + + end // if (CE == 1 && R == 0 && S == 0) + + end // always @ (negedge c_in) + + + specify + + if (C0) (C0 => Q) = (100, 100); + if (C1) (C1 => Q) = (100, 100); + specparam PATHPULSE$ = 0; + + endspecify + +endmodule // ODDR2 + diff --git a/fpga/usrp2/models/PLL_ADV.v b/fpga/usrp2/models/PLL_ADV.v new file mode 100644 index 000000000..d6a26e541 --- /dev/null +++ b/fpga/usrp2/models/PLL_ADV.v @@ -0,0 +1,2142 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/rainier/PLL_ADV.v,v 1.43.4.1 2007/12/07 01:25:16 yanx Exp $ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995/2004 Xilinx, Inc. +// All Right Reserved. +/////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor : Xilinx +// \ \ \/ Version : 10.1 +// \ \ Description : Xilinx Function Simulation Library Component +// / / Phase Lock Loop Clock +// /___/ /\ Filename : PLL_ADV.v +// \ \ / \ Timestamp : Thu Mar 25 16:44:07 PST 2004 +// \___\/\___\ +// +// Revision: +// 03/15/05 - Initial version. +// 10/14/05 - Add REL pin. +// 11/07/05 - Add PMCD. +// 12/02/05 - Change parameter default values. Add DRP read/write. +// 12/22/05 - CR 222805 222809 fix. +// 01/03/06 - Change RST_DEASSER_CLK value to CLKIN1 and CLKFB (BT#735). +// 01/11/06 - Remove GSR from reset logic of PLL (CR 223099). +// 01/26/06 - Add reset to locked logic (CR224502). +// 02/16/06 - Support -360 6o +360 phase shifting (CR 225765) +// 03/10/06 - Add parameter type declaration (CR 226003) +// 03/17/06 - Using assign/deassign to reset pll_locked_tmp2 and reduce +// lock time by MD (CR 224502). +// 04/19/06 - Change i to i1 and i2 in clkvco_lk. (CR230260). +// 07/17/06 - Remove i2 and first 4 clkvco_lk cycle generation (CR234931). +// 08/23/06 - Use clkout_en_tmp to generate clkout_en0; Use block statement to +// reset clock stop counter and flag. (CR422250) +// 09/19/06 - md_product update (CR 424286). +// 09/27/06 - Add error check for RESET_ON_LOSS_OF_LOCK (CR 425255). +// 11/10/06 - Keep 3 digits for real in duty cycle check function. (CR 428703). +// 01/12/07 - Add CLKOUT_DESKEW_ADJUST parameters (CR 432189). +// 03/30/07 - Fix error message for CLKSEL change in RST=0 (CR 436927). +// 04/09/07 - Enhance error message for RESET_ON_LOSS_OF_LOCK (CR 437405). +// 04/10/07 - Using assign/deassign to reset signals with # delay (CR 437660). +// 05/22/07 - Add setup check for REL (438781). +// 06/04/07 - Add wire declaration to internal signal. +// 06/8/07 - Generate clkfb_tst when GSR=0; +// - Chang VCOCLK_FREQ_MAX and VCOCLK_FREQ_MIN to parameter for simprim (BT1485). +// 06/18/07 - Improve error message for VCO (CR ). Initialize DRP mem (CR ). +// - Add CLKFBIN pulse width check (BT1476). +// 06/28/07 - Initial DRP memory (CR 434042), Error message improve (CR 438250). +// 07/11/07 - change daddr_in to 5 bits (CR 443757). +// 08/02/07 - Remove numbers from CLKOUT DESKEW_ADJUST check (CR443161). +// 08/21/07 - Not check CLKIN period when PMCD mode set (445101). +// Fix DUTY_CYCLE_MAX formula in case of divider larger than O_MAX_HT_LT (CR445945). +// Add warning if phase shift over pll ability (63 vco) (CR446037). +// 09/20/07 - Seperate fb_delay and delay_edge to handle 0 fb_delay (CR448938) +// 10/23/07 - Add warnings to initial phase shift calculation (CR448965) +// 11/01/07 - Remove zero check for CLKOUTx dly register bit15-8 (CR434042) +// 12/06/07 - Add I/O buf to simprim (CR456124) +// End Revision + + +`timescale 1 ps / 1 ps +`define PLL_LOCK_TIME 7 + + +module PLL_ADV ( + CLKFBDCM, + CLKFBOUT, + CLKOUT0, + CLKOUT1, + CLKOUT2, + CLKOUT3, + CLKOUT4, + CLKOUT5, + CLKOUTDCM0, + CLKOUTDCM1, + CLKOUTDCM2, + CLKOUTDCM3, + CLKOUTDCM4, + CLKOUTDCM5, + DO, + DRDY, + LOCKED, + CLKFBIN, + CLKIN1, + CLKIN2, + CLKINSEL, + DADDR, + DCLK, + DEN, + DI, + DWE, + REL, + RST +); + +parameter BANDWIDTH = "OPTIMIZED"; +parameter CLKFBOUT_DESKEW_ADJUST = "NONE"; +parameter CLKOUT0_DESKEW_ADJUST = "NONE"; +parameter CLKOUT1_DESKEW_ADJUST = "NONE"; +parameter CLKOUT2_DESKEW_ADJUST = "NONE"; +parameter CLKOUT3_DESKEW_ADJUST = "NONE"; +parameter CLKOUT4_DESKEW_ADJUST = "NONE"; +parameter CLKOUT5_DESKEW_ADJUST = "NONE"; +parameter integer CLKFBOUT_MULT = 1; +parameter real CLKFBOUT_PHASE = 0.0; +parameter real CLKIN1_PERIOD = 0.000; +parameter real CLKIN2_PERIOD = 0.000; +parameter integer CLKOUT0_DIVIDE = 1; +parameter real CLKOUT0_DUTY_CYCLE = 0.5; +parameter real CLKOUT0_PHASE = 0.0; +parameter integer CLKOUT1_DIVIDE = 1; +parameter real CLKOUT1_DUTY_CYCLE = 0.5; +parameter real CLKOUT1_PHASE = 0.0; +parameter integer CLKOUT2_DIVIDE = 1; +parameter real CLKOUT2_DUTY_CYCLE = 0.5; +parameter real CLKOUT2_PHASE = 0.0; +parameter integer CLKOUT3_DIVIDE = 1; +parameter real CLKOUT3_DUTY_CYCLE = 0.5; +parameter real CLKOUT3_PHASE = 0.0; +parameter integer CLKOUT4_DIVIDE = 1; +parameter real CLKOUT4_DUTY_CYCLE = 0.5; +parameter real CLKOUT4_PHASE = 0.0; +parameter integer CLKOUT5_DIVIDE = 1; +parameter real CLKOUT5_DUTY_CYCLE = 0.5; +parameter real CLKOUT5_PHASE = 0.0; +parameter COMPENSATION = "SYSTEM_SYNCHRONOUS"; +parameter integer DIVCLK_DIVIDE = 1; +parameter EN_REL = "FALSE"; +parameter PLL_PMCD_MODE = "FALSE"; +parameter real REF_JITTER = 0.100; +parameter RESET_ON_LOSS_OF_LOCK = "FALSE"; +parameter RST_DEASSERT_CLK = "CLKIN1"; + +localparam VCOCLK_FREQ_MAX = 1100; +localparam VCOCLK_FREQ_MIN = 400; + +output CLKFBDCM; +output CLKFBOUT; +output CLKOUT0; +output CLKOUT1; +output CLKOUT2; +output CLKOUT3; +output CLKOUT4; +output CLKOUT5; +output CLKOUTDCM0; +output CLKOUTDCM1; +output CLKOUTDCM2; +output CLKOUTDCM3; +output CLKOUTDCM4; +output CLKOUTDCM5; +output DRDY; +output LOCKED; +output [15:0] DO; + +input CLKFBIN; +input CLKIN1; +input CLKIN2; +input CLKINSEL; +input DCLK; +input DEN; +input DWE; +input REL; +input RST; +input [15:0] DI; +input [4:0] DADDR; + +localparam VCOCLK_FREQ_TARGET = 800; +localparam CLKIN_FREQ_MAX = 1000; +localparam CLKIN_FREQ_MIN = 1; //need check speed file, current is TBD +localparam CLKPFD_FREQ_MAX = 550; +localparam CLKPFD_FREQ_MIN = 1; //need check speed file, current is TBD +localparam M_MIN = 1; +localparam M_MAX = 74; +localparam D_MIN = 1; +localparam D_MAX = 52; +localparam O_MIN = 1; +localparam O_MAX = 128; +localparam O_MAX_HT_LT = 64; +localparam REF_CLK_JITTER_MAX = 350; +localparam REF_CLK_JITTER_SCALE = 0.1; +localparam MAX_FEEDBACK_DELAY = 10.0; +localparam MAX_FEEDBACK_DELAY_SCALE = 1.0; + +tri0 GSR = glbl.GSR; + +reg [4:0] daddr_lat; +reg valid_daddr; +reg drdy_out; +reg drp_lock, drp_lock1; +reg [15:0] dr_sram [31:0]; +reg [160:0] tmp_string; + +wire CLKFBIN, CLKIN1, CLKIN2, CLKINSEL ; +wire rst_in, RST, orig_rst_in ; +wire locked_out; +wire clkvco_lk_rst; + +reg clk0_out, clk1_out, clk2_out, clk3_out, clk4_out, clk5_out; +reg clkfb_out, clkfbm1_out; +reg clkout_en, clkout_en1, clkout_en0, clkout_en0_tmp; +integer clkout_cnt, clkin_cnt, clkin_lock_cnt; +integer clkout_en_time, locked_en_time, lock_cnt_max; +reg clkvco_lk, clkvco_free, clkvco; +reg fbclk_tmp; + +reg rst_in1, rst_unlock, rst_on_loss; +time rst_edge, rst_ht; + +reg fb_delay_found, fb_delay_found_tmp; +reg clkfb_tst; +real fb_delay_max; +time fb_delay, clkvco_delay, val_tmp, dly_tmp, fbm1_comp_delay; +time clkin_edge, delay_edge; + +real period_clkin; +integer clkin_period [4:0]; +integer period_vco, period_vco_half, period_vco_max, period_vco_min; +integer period_vco1, period_vco2, period_vco3, period_vco4; +integer period_vco5, period_vco6, period_vco7; +integer period_vco_target, period_vco_target_half; +integer period_fb, period_avg; + +real clkvco_freq_init_chk, clkfbm1pm_rl; +real tmp_real; +integer i, j, i1, i2; +integer md_product, md_product_dbl, clkin_stop_max, clkfb_stop_max; + +time pll_locked_delay, clkin_dly_t, clkfb_dly_t; +reg clkpll_dly, clkfbin_dly; +wire pll_unlock; +reg pll_locked_tmp1, pll_locked_tmp2; +reg lock_period; +reg pll_locked_tm, unlock_recover; +reg clkin_stopped_p, clkin_stopped_n; +reg clkfb_stopped_p, clkfb_stopped_n; +wire clkin_stopped, clkfb_stopped; +reg clkpll_jitter_unlock; +integer clkstop_cnt_p, clkstop_cnt_n, clkfbstop_cnt_p, clkfbstop_cnt_n; +integer clkin_jit, REF_CLK_JITTER_MAX_tmp; + +wire REL, DWE, DEN, DCLK, rel_o_mux_clk_tmp, clka1_in, clkb1_in; +wire init_trig, clkpll_tmp, clkpll, clk0in, clk1in, clk2in, clk3in, clk4in, clk5in; +wire clkfbm1in, clkfbm1ps_en; + + +reg clkout0_out; +reg clkout1_out; +reg clkout2_out; +reg clkout3_out; +reg clkout4_out; +reg clkout5_out; + +reg clka1_out, clkb1_out, clka1d2_out, clka1d4_out, clka1d8_out; +reg clkdiv_rel_rst, qrel_o_reg1, qrel_o_reg2, qrel_o_reg3, rel_o_mux_sel; +reg pmcd_mode; +reg chk_ok; + +wire rel_rst_o, rel_o_mux_clk; +wire clk0ps_en, clk1ps_en, clk2ps_en, clk3ps_en, clk4ps_en, clk5ps_en; + +reg [7:0] clkout_mux; +reg [2:0] clk0pm_sel, clk1pm_sel, clk2pm_sel, clk3pm_sel, clk4pm_sel, clk5pm_sel; +reg [2:0] clkfbm1pm_sel; +reg clk0_edge, clk1_edge, clk2_edge, clk3_edge, clk4_edge, clk5_edge; +reg clkfbm1_edge, clkind_edge; +reg clk0_nocnt, clk1_nocnt, clk2_nocnt, clk3_nocnt, clk4_nocnt, clk5_nocnt; +reg clkfbm1_nocnt, clkind_nocnt; +reg clkind_edget, clkind_nocntt; +reg [5:0] clk0_dly_cnt, clkout0_dly; +reg [5:0] clk1_dly_cnt, clkout1_dly; +reg [5:0] clk2_dly_cnt, clkout2_dly; +reg [5:0] clk3_dly_cnt, clkout3_dly; +reg [5:0] clk4_dly_cnt, clkout4_dly; +reg [5:0] clk5_dly_cnt, clkout5_dly; +reg [6:0] clk0_ht, clk0_lt; +reg [6:0] clk1_ht, clk1_lt; +reg [6:0] clk2_ht, clk2_lt; +reg [6:0] clk3_ht, clk3_lt; +reg [6:0] clk4_ht, clk4_lt; +reg [6:0] clk5_ht, clk5_lt; +reg [5:0] clkfbm1_dly_cnt, clkfbm1_dly; +reg [6:0] clkfbm1_ht, clkfbm1_lt; +reg [7:0] clkind_ht, clkind_lt; +reg [7:0] clkind_htt, clkind_ltt; +reg [7:0] clk0_ht1, clk0_cnt, clk0_div, clk0_div1; +reg [7:0] clk1_ht1, clk1_cnt, clk1_div, clk1_div1; +reg [7:0] clk2_ht1, clk2_cnt, clk2_div, clk2_div1; +reg [7:0] clk3_ht1, clk3_cnt, clk3_div, clk3_div1; +reg [7:0] clk4_ht1, clk4_cnt, clk4_div, clk4_div1; +reg [7:0] clk5_ht1, clk5_cnt, clk5_div, clk5_div1; +reg [7:0] clkfbm1_ht1, clkfbm1_cnt, clkfbm1_div, clkfbm1_div1; +reg [7:0] clkind_div; +reg [3:0] pll_cp, pll_res; +reg [1:0] pll_lfhf; +reg [1:0] pll_cpres = 2'b01; + +reg notifier; +wire [15:0] do_out, di_in; +wire clkin1_in, clkin2_in, clkfb_in, clkinsel_in, dwe_in, den_in, dclk_in; +wire [4:0] daddr_in; +wire rel_in, gsr_in, rst_input; + + assign #100 LOCKED = locked_out; + assign #100 DRDY = drdy_out; + assign #100 DO = do_out; + assign clkin1_in = CLKIN1; + assign clkin2_in = CLKIN2; + assign clkfb_in = CLKFBIN; + assign clkinsel_in = CLKINSEL; + assign rst_input = RST; + assign daddr_in = DADDR; + assign di_in = DI; + assign dwe_in = DWE; + assign den_in = DEN; + assign dclk_in = DCLK; + assign rel_in = REL; + + + +initial begin + #1; + if ($realtime == 0) begin + $display ("Simulator Resolution Error : Simulator resolution is set to a value greater than 1 ps."); + $display ("In order to simulate the PLL_ADV, the simulator resolution must be set to 1ps or smaller."); + $finish; + end +end + +initial begin + + case (COMPENSATION) + "SYSTEM_SYNCHRONOUS" : ; + "SOURCE_SYNCHRONOUS" : ; + "INTERNAL" : ; + "EXTERNAL" : ; + "DCM2PLL" : ; + "PLL2DCM" : ; + default : begin + $display("Attribute Syntax Error : The Attribute COMPENSATION on PLL_ADV instance %m is set to %s. Legal values for this attribute are SYSTEM_SYNCHRONOUS, SOURCE_SYNCHRONOUS, INTERNAL, EXTERNAL, DCM2PLL or PLL2DCM.", COMPENSATION); + $finish; + end + endcase + + case (BANDWIDTH) + "HIGH" : ; + "LOW" : ; + "OPTIMIZED" : ; + default : begin + $display("Attribute Syntax Error : The Attribute BANDWIDTH on PLL_ADV instance %m is set to %s. Legal values for this attribute are HIGH, LOW or OPTIMIZED.", BANDWIDTH); + $finish; + end + endcase + + case (CLKOUT0_DESKEW_ADJUST) + "NONE" : ; + "PPC" : ; + default : begin + $display("Attribute Syntax Error : The Attribute CLKOUT0_DESKEW_ADJUST on PLL_ADV instance %m is set to %s. Legal values for this attribute are NONE or PPC.", CLKOUT0_DESKEW_ADJUST); + $finish; + end + endcase + + case (CLKOUT1_DESKEW_ADJUST) + "NONE" : ; + "PPC" : ; + default : begin + $display("Attribute Syntax Error : The Attribute CLKOUT1_DESKEW_ADJUST on PLL_ADV instance %m is set to %s. Legal values for this attribute are NONE or PPC .", CLKOUT1_DESKEW_ADJUST); + $finish; + end + endcase + + case (CLKOUT2_DESKEW_ADJUST) + "NONE" : ; + "PPC" : ; + default : begin + $display("Attribute Syntax Error : The Attribute CLKOUT2_DESKEW_ADJUST on PLL_ADV instance %m is set to %s. Legal values for this attribute are NONE or PPC.", CLKOUT2_DESKEW_ADJUST); + $finish; + end + endcase + + case (CLKOUT3_DESKEW_ADJUST) + "NONE" : ; + "PPC" : ; + default : begin + $display("Attribute Syntax Error : The Attribute CLKOUT3_DESKEW_ADJUST on PLL_ADV instance %m is set to %s. Legal values for this attribute are NONE or PPC.", CLKOUT3_DESKEW_ADJUST); + $finish; + end + endcase + + case (CLKOUT4_DESKEW_ADJUST) + "NONE" : ; + "PPC" : ; + default : begin + $display("Attribute Syntax Error : The Attribute CLKOUT4_DESKEW_ADJUST on PLL_ADV instance %m is set to %s. Legal values for this attribute are NONE or PPC.", CLKOUT4_DESKEW_ADJUST); + $finish; + end + endcase + + case (CLKOUT5_DESKEW_ADJUST) + "NONE" : ; + "PPC" : ; + default : begin + $display("Attribute Syntax Error : The Attribute CLKOUT5_DESKEW_ADJUST on PLL_ADV instance %m is set to %s. Legal values for this attribute are NONE or PPC.", CLKOUT5_DESKEW_ADJUST); + $finish; + end + endcase + + case (CLKFBOUT_DESKEW_ADJUST) + "NONE" : ; + "PPC" : ; + default : begin + $display("Attribute Syntax Error : The Attribute CLKFBOUT_DESKEW_ADJUST on PLL_ADV instance %m is set to %s. Legal values for this attribute are NONE or PPC.", CLKFBOUT_DESKEW_ADJUST); + $finish; + end + endcase + + + case (PLL_PMCD_MODE) + "TRUE" : pmcd_mode = 1'b1; + "FALSE" : pmcd_mode = 1'b0; + default : begin + $display("Attribute Syntax Error : The Attribute PLL_PMCD_MODE on PLL_ADV instance %m is set to %s. Legal values for this attribute are FALSE or TRUE.", PLL_PMCD_MODE); + $finish; + end + endcase + + tmp_string = "CLKOUT0_DIVIDE"; + chk_ok = para_int_pmcd_chk(CLKOUT0_DIVIDE, tmp_string, 1, 128, pmcd_mode, 8); + tmp_string = "CLKOUT0_PHASE"; + chk_ok = para_real_pmcd_chk(CLKOUT0_PHASE, tmp_string, -360.0, 360.0, pmcd_mode, 0.0); + tmp_string = "CLKOUT0_DUTY_CYCLE"; + chk_ok = para_real_pmcd_chk(CLKOUT0_DUTY_CYCLE, tmp_string, 0.0, 1.0, pmcd_mode, 0.5); + + tmp_string = "CLKOUT1_DIVIDE"; + chk_ok = para_int_pmcd_chk(CLKOUT1_DIVIDE, tmp_string, 1, 128, pmcd_mode, 4); + tmp_string = "CLKOUT1_PHASE"; + chk_ok = para_real_pmcd_chk(CLKOUT1_PHASE, tmp_string, -360.0, 360.0, pmcd_mode, 0.0); + tmp_string = "CLKOUT1_DUTY_CYCLE"; + chk_ok = para_real_pmcd_chk(CLKOUT1_DUTY_CYCLE, tmp_string, 0.0, 1.0, pmcd_mode, 0.5); + + tmp_string = "CLKOUT2_DIVIDE"; + chk_ok = para_int_pmcd_chk(CLKOUT2_DIVIDE, tmp_string, 1, 128, pmcd_mode, 2); + tmp_string = "CLKOUT2_PHASE"; + chk_ok = para_real_pmcd_chk(CLKOUT2_PHASE, tmp_string, -360.0, 360.0, pmcd_mode, 0.0); + tmp_string = "CLKOUT2_DUTY_CYCLE"; + chk_ok = para_real_pmcd_chk(CLKOUT2_DUTY_CYCLE, tmp_string, 0.0, 1.0, pmcd_mode, 0.5); + + tmp_string = "CLKOUT3_DIVIDE"; + chk_ok = para_int_pmcd_chk(CLKOUT3_DIVIDE, tmp_string, 1, 128, pmcd_mode, 1); + tmp_string = "CLKOUT3_PHASE"; + chk_ok = para_real_pmcd_chk(CLKOUT3_PHASE, tmp_string, -360.0, 360.0, pmcd_mode, 0.0); + tmp_string = "CLKOUT3_DUTY_CYCLE"; + chk_ok = para_real_pmcd_chk(CLKOUT3_DUTY_CYCLE, tmp_string, 0.0, 1.0, pmcd_mode, 0.5); + + tmp_string = "CLKOUT4_DIVIDE"; + chk_ok = para_int_range_chk(CLKOUT4_DIVIDE, tmp_string, 1, 128); + tmp_string = "CLKOUT4_PHASE"; + chk_ok = para_real_range_chk(CLKOUT4_PHASE, tmp_string, -360.0, 360.0); + tmp_string = "CLKOUT4_DUTY_CYCLE"; + chk_ok = para_real_range_chk(CLKOUT4_DUTY_CYCLE, tmp_string, 0.0, 1.0); + + tmp_string = "CLKOUT5_DIVIDE"; + chk_ok = para_int_range_chk (CLKOUT5_DIVIDE, tmp_string, 1, 128); + tmp_string = "CLKOUT5_PHASE"; + chk_ok = para_real_range_chk(CLKOUT5_PHASE, tmp_string, -360.0, 360.0); + tmp_string = "CLKOUT5_DUTY_CYCLE"; + chk_ok = para_real_range_chk (CLKOUT5_DUTY_CYCLE, tmp_string, 0.0, 1.0); + + tmp_string = "CLKFBOUT_MULT"; + chk_ok = para_int_pmcd_chk(CLKFBOUT_MULT, tmp_string, 1, 74, pmcd_mode, 1); + tmp_string = "CLKFBOUT_PHASE"; + chk_ok = para_real_pmcd_chk(CLKFBOUT_PHASE, tmp_string, -360.0, 360.0, pmcd_mode, 0.0); + tmp_string = "DIVCLK_DIVIDE"; + chk_ok = para_int_range_chk (DIVCLK_DIVIDE, tmp_string, 1, 52); + + tmp_string = "REF_JITTER"; + chk_ok = para_real_range_chk (REF_JITTER, tmp_string, 0.0, 0.999); + if (((CLKIN1_PERIOD < 1.0) || (CLKIN1_PERIOD > 52.630)) && (pmcd_mode == 0)) begin + $display("Attribute Syntax Error : CLKIN1_PERIOD is not in range 1.0 ... 52.630."); + end + + if (((CLKIN2_PERIOD < 1.0) || (CLKIN2_PERIOD > 52.630)) && (pmcd_mode == 0)) begin + $display("Attribute Syntax Error : CLKIN1_PERIOD is not in range 1.0 ... 52.630."); + end + + + case (RESET_ON_LOSS_OF_LOCK) + "FALSE" : rst_on_loss = 1'b0; +// "TRUE" : if (pmcd_mode) rst_on_loss = 1'b0; else rst_on_loss = 1'b1; + default : begin + $display("Attribute Syntax Error : The Attribute RESET_ON_LOSS_OF_LOCK on PLL_ADV instance %m is set to %s. This attribute must always be set to FALSE for X_PLL_ADV to function correctly. Please correct the setting for the attribute and re-run the simulation.", RESET_ON_LOSS_OF_LOCK); + $finish; + end + endcase + + case (CLKFBOUT_MULT) + 1 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1101; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b0101; pll_res = 4'b1111; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b0101; pll_res = 4'b1111; pll_lfhf = 2'b11; end + 2 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1110; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1111; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b1111; pll_lfhf = 2'b11; end + 3 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0110; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b0111; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b0110; pll_res = 4'b0101; pll_lfhf = 2'b11; end + 4 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1101; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b0111; pll_res = 4'b1001; pll_lfhf = 2'b11; end + 5 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b0101; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1101; pll_res = 4'b1001; pll_lfhf = 2'b11; end + 6 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b0101; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b0111; pll_res = 4'b0001; pll_lfhf = 2'b11; end + 7 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1001; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b0001; pll_lfhf = 2'b11; end + 8 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1110; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1111; pll_res = 4'b1110; pll_lfhf = 2'b11; end + 9 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1110; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b0001; pll_lfhf = 2'b11; end + 10 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b0001; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1111; pll_res = 4'b0001; pll_lfhf = 2'b11; end + 11 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b0001; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1101; pll_res = 4'b0110; pll_lfhf = 2'b11; end + 12 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b0110; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b0110; pll_lfhf = 2'b11; end + 13 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b0110; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b0110; pll_lfhf = 2'b11; end + 14 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b0110; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1111; pll_res = 4'b0110; pll_lfhf = 2'b11; end + 15 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH === "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b1010; pll_lfhf = 2'b11; end + 16 : if (BANDWIDTH === "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b1010; pll_lfhf = 2'b11; end + 17 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + 18 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + 19 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + 20 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b1100; pll_lfhf = 2'b11; end + 21 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b1100; pll_lfhf = 2'b11; end + 22 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1101; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1101; pll_res = 4'b1100; pll_lfhf = 2'b11; end + 23 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1101; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1101; pll_res = 4'b1100; pll_lfhf = 2'b11; end + 24 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1101; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0111; pll_res = 4'b0010; pll_lfhf = 2'b11; end + 25 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b1100; pll_lfhf = 2'b11; end + 26 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1110; pll_res = 4'b1100; pll_lfhf = 2'b11; end + 27 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1111; pll_res = 4'b1100; pll_lfhf = 2'b11; end + 28 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b0010; pll_lfhf = 2'b11; end + 29 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b0010; pll_lfhf = 2'b11; end + 30 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0001; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1110; pll_res = 4'b1100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b0010; pll_lfhf = 2'b11; end + 31 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1100; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b0010; pll_lfhf = 2'b11; end + 32 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1100; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b1100; pll_res = 4'b0010; pll_lfhf = 2'b11; end + 33 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b1111; pll_res = 4'b1010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 34 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0111; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 35 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0111; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 36 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0111; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 37 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0110; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 38 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0110; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 39 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 40 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 41 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 42 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 43 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 44 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0100; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 45 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 46 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 47 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0101; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 48 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0101; pll_res = 4'b0010; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 49 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 50 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 51 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 52 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 53 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 54 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 55 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 56 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0011; pll_res = 4'b0100; pll_lfhf = 2'b11; end + 57 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 58 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 59 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 60 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 61 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 62 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 63 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + 64 : if (BANDWIDTH == "LOW") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "HIGH") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + else if (BANDWIDTH == "OPTIMIZED") begin pll_cp = 4'b0010; pll_res = 4'b1000; pll_lfhf = 2'b11; end + endcase + + + tmp_string = "DIVCLK_DIVIDE"; + chk_ok = para_int_range_chk (DIVCLK_DIVIDE, tmp_string, D_MIN, D_MAX); + + tmp_string = "CLKFBOUT_MULT"; + chk_ok = para_int_range_chk (CLKFBOUT_MULT, tmp_string, M_MIN, M_MAX); + + tmp_string = "CLKOUT0_DUTY_CYCLE"; + chk_ok = clkout_duty_chk (CLKOUT0_DIVIDE, CLKOUT0_DUTY_CYCLE, tmp_string); + tmp_string = "CLKOUT1_DUTY_CYCLE"; + chk_ok = clkout_duty_chk (CLKOUT1_DIVIDE, CLKOUT1_DUTY_CYCLE, tmp_string); + tmp_string = "CLKOUT2_DUTY_CYCLE"; + chk_ok = clkout_duty_chk (CLKOUT2_DIVIDE, CLKOUT2_DUTY_CYCLE, tmp_string); + tmp_string = "CLKOUT3_DUTY_CYCLE"; + chk_ok = clkout_duty_chk (CLKOUT3_DIVIDE, CLKOUT3_DUTY_CYCLE, tmp_string); + tmp_string = "CLKOUT4_DUTY_CYCLE"; + chk_ok = clkout_duty_chk (CLKOUT4_DIVIDE, CLKOUT4_DUTY_CYCLE, tmp_string); + tmp_string = "CLKOUT5_DUTY_CYCLE"; + chk_ok = clkout_duty_chk (CLKOUT5_DIVIDE, CLKOUT5_DUTY_CYCLE, tmp_string); + + period_vco_max = 1000000 / VCOCLK_FREQ_MIN; + period_vco_min = 1000000 / VCOCLK_FREQ_MAX; + period_vco_target = 1000000 / VCOCLK_FREQ_TARGET; + period_vco_target_half = period_vco_target / 2; + fb_delay_max = MAX_FEEDBACK_DELAY * MAX_FEEDBACK_DELAY_SCALE; + md_product = CLKFBOUT_MULT * DIVCLK_DIVIDE; + md_product_dbl = md_product * 2; + clkout_en_time = `PLL_LOCK_TIME + 2; +// locked_en_time = md_product_dbl + clkout_en_time +2; // for DCM 3 cycle reset requirement + locked_en_time = md_product + clkout_en_time + 2; // for DCM 3 cycle reset requirement + lock_cnt_max = locked_en_time + 6; + clkfb_stop_max = 3; + clkin_stop_max = DIVCLK_DIVIDE + 1; + REF_CLK_JITTER_MAX_tmp = REF_CLK_JITTER_MAX; + + clk_out_para_cal (clk0_ht, clk0_lt, clk0_nocnt, clk0_edge, CLKOUT0_DIVIDE, CLKOUT0_DUTY_CYCLE); + clk_out_para_cal (clk1_ht, clk1_lt, clk1_nocnt, clk1_edge, CLKOUT1_DIVIDE, CLKOUT1_DUTY_CYCLE); + clk_out_para_cal (clk2_ht, clk2_lt, clk2_nocnt, clk2_edge, CLKOUT2_DIVIDE, CLKOUT2_DUTY_CYCLE); + clk_out_para_cal (clk3_ht, clk3_lt, clk3_nocnt, clk3_edge, CLKOUT3_DIVIDE, CLKOUT3_DUTY_CYCLE); + clk_out_para_cal (clk4_ht, clk4_lt, clk4_nocnt, clk4_edge, CLKOUT4_DIVIDE, CLKOUT4_DUTY_CYCLE); + clk_out_para_cal (clk5_ht, clk5_lt, clk5_nocnt, clk5_edge, CLKOUT5_DIVIDE, CLKOUT5_DUTY_CYCLE); + clk_out_para_cal (clkfbm1_ht, clkfbm1_lt, clkfbm1_nocnt, clkfbm1_edge, CLKFBOUT_MULT, 0.50); + clk_out_para_cal (clkind_ht, clkind_lt, clkind_nocnt, clkind_edge, DIVCLK_DIVIDE, 0.50); + tmp_string = "CLKOUT0_PHASE"; + clkout_dly_cal (clkout0_dly, clk0pm_sel, CLKOUT0_DIVIDE, CLKOUT0_PHASE, tmp_string); + tmp_string = "CLKOUT1_PHASE"; + clkout_dly_cal (clkout1_dly, clk1pm_sel, CLKOUT1_DIVIDE, CLKOUT1_PHASE, tmp_string); + tmp_string = "CLKOUT2_PHASE"; + clkout_dly_cal (clkout2_dly, clk2pm_sel, CLKOUT2_DIVIDE, CLKOUT2_PHASE, tmp_string); + tmp_string = "CLKOUT3_PHASE"; + clkout_dly_cal (clkout3_dly, clk3pm_sel, CLKOUT3_DIVIDE, CLKOUT3_PHASE, tmp_string); + tmp_string = "CLKOUT4_PHASE"; + clkout_dly_cal (clkout4_dly, clk4pm_sel, CLKOUT4_DIVIDE, CLKOUT4_PHASE, tmp_string); + tmp_string = "CLKOUT5_PHASE"; + clkout_dly_cal (clkout5_dly, clk5pm_sel, CLKOUT5_DIVIDE, CLKOUT5_PHASE, tmp_string); + tmp_string = "CLKFBOUT_PHASE"; + clkout_dly_cal (clkfbm1_dly, clkfbm1pm_sel, CLKFBOUT_MULT, CLKFBOUT_PHASE, tmp_string); + + clkind_div = DIVCLK_DIVIDE; + + dr_sram[5'b11100] = {8'bx, clk0_edge, clk0_nocnt, clkout0_dly[5:0]}; + dr_sram[5'b11011] = {clk0pm_sel[2:0], 1'b1, clk0_ht[5:0], clk0_lt[5:0]}; + dr_sram[5'b11010] = {8'bx, clk1_edge, clk1_nocnt, clkout1_dly[5:0]}; + dr_sram[5'b11001] = {clk1pm_sel[2:0], 1'b1, clk1_ht[5:0], clk1_lt[5:0]}; + dr_sram[5'b10111] = {8'bx, clk2_edge, clk2_nocnt, clkout2_dly[5:0]}; + dr_sram[5'b10110] = {clk2pm_sel[2:0], 1'b1, clk2_ht[5:0], clk2_lt[5:0]}; + dr_sram[5'b10101] = {8'bx, clk3_edge, clk3_nocnt, clkout3_dly[5:0]}; + dr_sram[5'b10100] = {clk3pm_sel[2:0], 1'b1, clk3_ht[5:0], clk3_lt[5:0]}; + dr_sram[5'b10011] = {8'bx, clk4_edge, clk4_nocnt, clkout4_dly[5:0]}; + dr_sram[5'b10010] = {clk4pm_sel[2:0], 1'b1, clk4_ht[5:0], clk4_lt[5:0]}; + dr_sram[5'b01111] = {8'bx, clk5_edge, clk5_nocnt, clkout5_dly[5:0]}; + dr_sram[5'b01110] = {clk5pm_sel[2:0], 1'b1, clk5_ht[5:0], clk5_lt[5:0]}; + dr_sram[5'b01101] = {8'bx, clkfbm1_edge, clkfbm1_nocnt, clkfbm1_dly[5:0]}; + dr_sram[5'b01100] = {clkfbm1pm_sel[2:0], 1'b1, clkfbm1_ht[5:0], clkfbm1_lt[5:0]}; + dr_sram[5'b00110] = {2'bx, clkind_edge, clkind_nocnt, clkind_ht[5:0], clkind_lt[5:0]}; + dr_sram[5'b00001] = {8'bx, pll_lfhf, pll_cpres, pll_cp}; + dr_sram[5'b00000] = {6'bx, pll_res, 6'bx}; + + +// **** PMCD ******* + +//*** Clocks MUX + + case (RST_DEASSERT_CLK) + "CLKIN1" : rel_o_mux_sel = 1'b1; + "CLKFBIN" : rel_o_mux_sel = 1'b0; + default : begin + $display("Attribute Syntax Error : The attribute RST_DEASSERT_CLK on PLL_ADV instance %m is set to %s. Legal values for this attribute are CLKIN1 and CLKFBIN.", RST_DEASSERT_CLK); + $finish; + end + endcase + +//*** CLKDIV_RST + case (EN_REL) + "FALSE" : clkdiv_rel_rst = 1'b0; + "TRUE" : clkdiv_rel_rst = 1'b1; + default : begin + $display("Attribute Syntax Error : The attribute EN_REL on PLL_ADV instance %m is set to %s. Legal values for this attribute are TRUE or FALSE.", EN_REL); + $finish; + end + endcase + + +end + +initial begin + rst_in1 = 0; + rst_unlock = 0; + clkin_period[0] = 0; + clkin_period[1] = 0; + clkin_period[2] = 0; + clkin_period[3] = 0; + clkin_period[4] = 0; + period_avg = 0; + period_fb = 0; + fb_delay = 0; + clkfbm1_div = 1; + clkfbm1_div1 = 0; + clkvco_delay = 0; + fbm1_comp_delay = 0; + clkfbm1pm_rl = 0; + period_vco = 0; + period_vco1 = 0; + period_vco2 = 0; + period_vco3 = 0; + period_vco4 = 0; + period_vco5 = 0; + period_vco6 = 0; + period_vco7 = 0; + period_vco_half = 0; + fb_delay_found = 0; + fb_delay_found_tmp = 0; + clkin_edge = 0; + delay_edge = 0; + clkvco_free = 0; + clkvco_lk = 0; + fbclk_tmp = 0; + clkfb_tst = 0; + clkout_cnt = 0; + clkout_en = 0; + clkout_en0 = 0; + clkout_en0_tmp = 0; + clkout_en1 = 0; + pll_locked_tmp1 = 0; + pll_locked_tmp2 = 0; + pll_locked_tm = 0; + pll_locked_delay = 0; + clkout_mux = 3'b0; + unlock_recover = 0; + clkstop_cnt_p = 0; + clkstop_cnt_n = 0; + clkpll_jitter_unlock = 0; + clkin_jit = 0; + clkin_cnt = 0; + clkin_lock_cnt = 0; + clkin_stopped_p = 0; + clkin_stopped_n = 0; + clkfb_stopped_p = 0; + clkfb_stopped_n = 0; + clkpll_dly = 0; + clkfbin_dly = 0; + clkfbstop_cnt_p = 0; + clkfbstop_cnt_n = 0; + lock_period = 0; + rst_edge = 0; + rst_ht = 0; + drdy_out = 0; + drp_lock = 0; + clkout0_out = 0; + clkout1_out = 0; + clkout2_out = 0; + clkout3_out = 0; + clkout4_out = 0; + clkout5_out = 0; + clka1_out = 1'b0; + clkb1_out = 1'b0; + clka1d2_out = 1'b0; + clka1d4_out = 1'b0; + clka1d8_out = 1'b0; + qrel_o_reg1 = 1'b0; + qrel_o_reg2 = 1'b0; + qrel_o_reg3 = 1'b0; + clk0_dly_cnt = 6'b0; + clk1_dly_cnt = 6'b0; + clk2_dly_cnt = 6'b0; + clk3_dly_cnt = 6'b0; + clk4_dly_cnt = 6'b0; + clk5_dly_cnt = 6'b0; + clkfbm1_dly_cnt = 6'b0; + clk0_cnt = 8'b0; + clk1_cnt = 8'b0; + clk2_cnt = 8'b0; + clk3_cnt = 8'b0; + clk4_cnt = 8'b0; + clk5_cnt = 8'b0; + clkfbm1_cnt = 8'b0; + clk0_out = 0; + clk1_out = 0; + clk2_out = 0; + clk3_out = 0; + clk4_out = 0; + clk5_out = 0; + clkfb_out = 0; + clkfbm1_out = 0; +end + +// PMCD function + +//*** asyn RST + always @(orig_rst_in) + if (orig_rst_in == 1'b1) begin + assign qrel_o_reg1 = 1'b1; + assign qrel_o_reg2 = 1'b1; + assign qrel_o_reg3 = 1'b1; + end + else if (orig_rst_in == 1'b0) begin + deassign qrel_o_reg1; + deassign qrel_o_reg2; + deassign qrel_o_reg3; + end + +//*** Clocks MUX + + assign rel_o_mux_clk_tmp = rel_o_mux_sel ? clkin1_in : clkfb_in; + assign rel_o_mux_clk = (pmcd_mode) ? rel_o_mux_clk_tmp : 0; + assign clka1_in = (pmcd_mode) ? clkin1_in : 0; + assign clkb1_in = (pmcd_mode) ? clkfb_in : 0; + + +//*** Rel and Rst + always @(posedge rel_o_mux_clk) + qrel_o_reg1 <= 1'b0; + + always @(negedge rel_o_mux_clk) + qrel_o_reg2 <= qrel_o_reg1; + + always @(posedge rel_in) + qrel_o_reg3 <= 1'b0; + + assign rel_rst_o = clkdiv_rel_rst ? (qrel_o_reg3 || qrel_o_reg1) : qrel_o_reg1; + +//*** CLKA + always @(clka1_in or qrel_o_reg2) + if (qrel_o_reg2 == 1'b1) + clka1_out <= 1'b0; + else if (qrel_o_reg2 == 1'b0) + clka1_out <= clka1_in; + +//*** CLKB + always @(clkb1_in or qrel_o_reg2) + if (qrel_o_reg2 == 1'b1) + clkb1_out <= 1'b0; + else if (qrel_o_reg2 == 1'b0) + clkb1_out <= clkb1_in; + + +//*** Clock divider + always @(posedge clka1_in or posedge rel_rst_o) + if (rel_rst_o == 1'b1) + clka1d2_out <= 1'b0; + else if (rel_rst_o == 1'b0) + clka1d2_out <= ~clka1d2_out; + + always @(posedge clka1d2_out or posedge rel_rst_o) + if (rel_rst_o == 1'b1) + clka1d4_out <= 1'b0; + else if (rel_rst_o == 1'b0) + clka1d4_out <= ~clka1d4_out; + + always @(posedge clka1d4_out or posedge rel_rst_o) + if (rel_rst_o == 1'b1) + clka1d8_out <= 1'b0; + else if (rel_rst_o == 1'b0) + clka1d8_out <= ~clka1d8_out; + + assign CLKOUT5 = (pmcd_mode) ? 0 : clkout5_out; + assign CLKOUT4 = (pmcd_mode) ? 0 : clkout4_out; + assign CLKOUT3 = (pmcd_mode) ? clka1_out : clkout3_out; + assign CLKOUT2 = (pmcd_mode) ? clka1d2_out : clkout2_out; + assign CLKOUT1 = (pmcd_mode) ? clka1d4_out : clkout1_out; + assign CLKOUT0 = (pmcd_mode) ? clka1d8_out : clkout0_out; + assign CLKFBOUT = (pmcd_mode) ? clkb1_out : clkfb_out; + assign CLKOUTDCM5 = (pmcd_mode) ? 0 : clkout5_out; + assign CLKOUTDCM4 = (pmcd_mode) ? 0 : clkout4_out; + assign CLKOUTDCM3 = (pmcd_mode) ? clka1_out : clkout3_out; + assign CLKOUTDCM2 = (pmcd_mode) ? clka1d2_out : clkout2_out; + assign CLKOUTDCM1 = (pmcd_mode) ? clka1d4_out : clkout1_out; + assign CLKOUTDCM0 = (pmcd_mode) ? clka1d8_out : clkout0_out; + assign CLKFBDCM = (pmcd_mode) ? clkb1_out : clkfb_out; + +// PLL function + +always @(clkinsel_in ) + if (pmcd_mode != 1) begin + if ($time >1 && rst_in != 1'b1) begin + $display("Input Error : PLL input clock can only be switched when RST=1. CLKINSEL on instance %m at time %t changed when RST low, should change at RST high.", $time); + $finish; + end + if (clkinsel_in ==1) begin + if (CLKIN1_PERIOD > (1000.0 /CLKIN_FREQ_MIN) || CLKIN1_PERIOD < (1000.0 / CLKIN_FREQ_MAX)) begin + $display (" Attribute Syntax Error : The attribute CLKIN1_PERIOD is set to %f ns and out the allowed range %f ns to %f ns.", CLKIN1_PERIOD, 1000.0/CLKIN_FREQ_MAX, 1000.0/CLKIN_FREQ_MIN); + $finish; + end + end + else if (clkinsel_in ==0) begin + if (CLKIN2_PERIOD > (1000.0 /CLKIN_FREQ_MIN) || CLKIN2_PERIOD < (1000.0 / CLKIN_FREQ_MAX)) begin + $display (" Attribute Syntax Error : The attribute CLKIN2_PERIOD is set to %f ns and out the allowed range %f ns to %f ns.", CLKIN2_PERIOD, 1000.0/CLKIN_FREQ_MAX, 1000.0/CLKIN_FREQ_MIN); + $finish; + end + end + + period_clkin = (clkinsel_in) ? CLKIN1_PERIOD : CLKIN2_PERIOD; + clkvco_freq_init_chk = 1000.0 * CLKFBOUT_MULT / (period_clkin * DIVCLK_DIVIDE); + + if (clkvco_freq_init_chk > VCOCLK_FREQ_MAX || clkvco_freq_init_chk < VCOCLK_FREQ_MIN) begin + $display (" Attribute Syntax Error : The calculation of VCO frequency=%f Mhz. This exceeds the permitted VCO frequency range of %f Mhz to %f Mhz. The VCO frequency is calculated with formula: VCO frequency = CLKFBOUT_MULT / (DIVCLK_DIVIDE * CLKIN_PERIOD). Please adjust the attributes to the permitted VCO frequency range.", clkvco_freq_init_chk, VCOCLK_FREQ_MIN, VCOCLK_FREQ_MAX); + $finish; + end +end + + assign init_trig = 1; + + + assign clkpll_tmp = (clkinsel_in) ? clkin1_in : clkin2_in; + assign clkpll = (pmcd_mode) ? 0 : clkpll_tmp; + + assign orig_rst_in = rst_input; + +always @(posedge clkpll or posedge orig_rst_in) + if (orig_rst_in) + rst_in1 <= 1; + else + rst_in1 <= orig_rst_in; + + assign rst_in = (rst_in1 || rst_unlock); + + always @(posedge pll_unlock) + if (rst_on_loss ) begin + rst_unlock <= 1'b1; + rst_unlock <= #10000 1'b0; + end + +always @(rst_input ) + if (rst_input==1) + rst_edge = $time; + else if (rst_input==0 && rst_edge > 1) begin + rst_ht = $time - rst_edge; + if (rst_ht < 10000) + $display("Input Error : RST on instance %m at time %t must be asserted at least for 10 ns.", $time); + end + +// +// DRP port read and write +// + + assign do_out = dr_sram[daddr_lat]; + +always @(posedge dclk_in or posedge gsr_in) + if (gsr_in == 1) begin + drp_lock <= 0; + end + else begin + if (den_in == 1) begin + valid_daddr = addr_is_valid(daddr_in); + if (drp_lock == 1) begin + $display(" Warning : DEN is high at PLL_ADV instance %m at time %t. Need wait for DRDY signal before next read/write operation through DRP. ", $time); + $finish; + end + else begin + drp_lock <= 1; + daddr_lat <= daddr_in; + end + + if (valid_daddr && ( daddr_in == 5'b00110 || daddr_in == 5'b00001 || daddr_in == 5'b00000 || + (daddr_in >= 5'b01100 && daddr_in <= 5'b11100 && daddr_in != 5'b10000 && + daddr_in != 5'b10001 && daddr_in != 5'b11000 ))) begin + end + else begin + $display(" Warning : Address DADDR=%b is unsupported at PLL_ADV instance %m at time %t. ", DADDR, $time); + end + + if (dwe_in == 1) begin // write process + if (rst_input == 1) begin + if (valid_daddr && ( daddr_in == 5'b00110 || daddr_in == 5'b00001 || daddr_in == 5'b00000 || + (daddr_in >= 5'b01100 && daddr_in <= 5'b11100 && daddr_in != 5'b10000 && + daddr_in != 5'b10001 && daddr_in != 5'b11000 ))) begin + dr_sram[daddr_in] <= di_in; + end + + if (daddr_in == 5'b11100) + clkout_delay_para_drp (clkout0_dly, clk0_nocnt, clk0_edge, di_in, daddr_in); + + if (daddr_in == 5'b11011) + clkout_hl_para_drp (clk0_lt, clk0_ht, clk0pm_sel, di_in, daddr_in); + + if (daddr_in == 5'b11010) + clkout_delay_para_drp (clkout1_dly, clk1_nocnt, clk1_edge, di_in, daddr_in); + + if (daddr_in == 5'b11001) + clkout_hl_para_drp (clk1_lt, clk1_ht, clk1pm_sel, di_in, daddr_in); + + if (daddr_in == 5'b10111) + clkout_delay_para_drp (clkout2_dly, clk2_nocnt, clk2_edge, di_in, daddr_in); + + if (daddr_in == 5'b10110) + clkout_hl_para_drp (clk2_lt, clk2_ht, clk2pm_sel, di_in, daddr_in); + + if (daddr_in == 5'b10101) + clkout_delay_para_drp (clkout3_dly, clk3_nocnt, clk3_edge, di_in, daddr_in); + + if (daddr_in == 5'b10100) + clkout_hl_para_drp (clk3_lt, clk3_ht, clk3pm_sel, di_in, daddr_in); + + if (daddr_in == 5'b10011) + clkout_delay_para_drp (clkout4_dly, clk4_nocnt, clk4_edge, di_in, daddr_in); + + if (daddr_in == 5'b10010) + clkout_hl_para_drp (clk4_lt, clk4_ht, clk4pm_sel, di_in, daddr_in); + + if (daddr_in == 5'b01111) + clkout_delay_para_drp (clkout5_dly, clk5_nocnt, clk5_edge, di_in, daddr_in); + + if (daddr_in == 5'b01110) + clkout_hl_para_drp (clk5_lt, clk5_ht, clk5pm_sel, di_in, daddr_in); + + if (daddr_in == 5'b01101) + clkout_delay_para_drp (clkfbm1_dly, clkfbm1_nocnt, clkfbm1_edge, di_in, daddr_in); + + if (daddr_in == 5'b01100) + clkout_hl_para_drp (clkfbm1_lt, clkfbm1_ht, clkfbm1pm_sel, di_in, daddr_in); + + if (daddr_in == 5'b00110) begin + clkind_lt <= di_in[5:0]; + clkind_ht <= di_in[11:6]; + if ( di_in[5:0] == 6'b0 && di_in[11:6] == 6'b0 ) + clkind_div <= 8'b10000000; + else if (di_in[5:0] == 6'b0 && di_in[11:6] != 6'b0 ) + clkind_div <= 64 + di_in[11:6]; + else if (di_in[5:0] == 6'b0 && di_in[11:6] != 6'b0 ) + clkind_div <= 64 + di_in[5:0]; + else + clkind_div <= di_in[5:0] + di_in[11:6]; + clkind_nocnt <= di_in[12]; + clkind_edge <= di_in[13]; + end + + end + else begin + $display(" Error : RST is low at PLL_ADV instance %m at time %t. RST need to be high when change X_PLL_ADV paramters through DRP. ", $time); + end + + end //DWE + + end //DEN + if ( drp_lock == 1) begin + drp_lock <= 0; + drp_lock1 <= 1; + end + if (drp_lock1 == 1) begin + drp_lock1 <= 0; + drdy_out <= 1; + end + if (drdy_out == 1) + drdy_out <= 0; +end + +function addr_is_valid; +input [6:0] daddr_funcin; +begin + addr_is_valid = 1; + for (i=0; i<=6; i=i+1) + if ( daddr_funcin[i] != 0 && daddr_funcin[i] != 1) + addr_is_valid = 0; +end +endfunction + + +// end process drp; + + +// +// determine clock period +// + + always @(posedge clkpll or posedge rst_in) + if (rst_in) + begin + clkin_period[0] <= period_vco_target; + clkin_period[1] <= period_vco_target; + clkin_period[2] <= period_vco_target; + clkin_period[3] <= period_vco_target; + clkin_period[4] <= period_vco_target; + clkin_jit <= 0; + clkin_lock_cnt <= 0; + pll_locked_tm <= 0; + lock_period <= 0; + pll_locked_tmp1 <= 0; + clkout_en0_tmp <= 0; + unlock_recover <= 0; + clkin_edge <= 0; + end + else begin + clkin_edge <= $time; + clkin_period[4] <= clkin_period[3]; + clkin_period[3] <= clkin_period[2]; + clkin_period[2] <= clkin_period[1]; + clkin_period[1] <= clkin_period[0]; + if (clkin_edge != 0 && clkin_stopped_p == 0 && clkin_stopped_n == 0) + clkin_period[0] <= $time - clkin_edge; + + if (pll_unlock == 0) + clkin_jit <= $time - clkin_edge - clkin_period[0]; + else + clkin_jit <= 0; + + if ( (clkin_lock_cnt < lock_cnt_max) && fb_delay_found && pll_unlock == 0) + clkin_lock_cnt <= clkin_lock_cnt + 1; + else if (pll_unlock == 1 && rst_on_loss ==0 && pll_locked_tmp1 ==1 ) begin + clkin_lock_cnt <= locked_en_time; + unlock_recover <= 1; + end + + if ( clkin_lock_cnt >= `PLL_LOCK_TIME && pll_unlock == 0) + pll_locked_tm <= 1; + + if ( clkin_lock_cnt == 6 ) + lock_period <= 1; + + if (clkin_lock_cnt >= clkout_en_time) begin + clkout_en0_tmp <= 1; + end + + if (clkin_lock_cnt >= locked_en_time) + pll_locked_tmp1 <= 1; + + if (unlock_recover ==1 && clkin_lock_cnt >= lock_cnt_max) + unlock_recover <= 0; + end + + always @(clkout_en0_tmp) + if (clkout_en0_tmp==0) + clkout_en0 = 0; + else + @(negedge clkpll) + clkout_en0 <= #(clkin_period[0]/2) clkout_en0_tmp; + + always @(clkout_en0) + clkout_en <= #(clkvco_delay) clkout_en0; + + always @(pll_locked_tmp1 ) + if (pll_locked_tmp1==0) + pll_locked_tmp2 = pll_locked_tmp1; + else begin + pll_locked_tmp2 <= #pll_locked_delay pll_locked_tmp1; + end + + + always @(rst_in) + if (rst_in) begin + assign pll_locked_tmp2 = 0; + assign clkout_en0 = 0; + assign clkout_en = 0; + end + else begin + deassign pll_locked_tmp2; + deassign clkout_en0; + deassign clkout_en; + end + + assign locked_out = (pll_locked_tm && pll_locked_tmp2 && ~pll_unlock && !unlock_recover) ? 1 : 0; + + + always @(clkin_period[0] or clkin_period[1] or clkin_period[2] or + clkin_period[3] or clkin_period[4] or period_avg) + if ( clkin_period[0] != period_avg) + period_avg = (clkin_period[0] + clkin_period[1] + clkin_period[2] + + clkin_period[3] + clkin_period[4])/5; + + always @(period_avg or clkind_div or clkfbm1_div) begin + period_fb = period_avg * clkind_div; + period_vco = period_fb / clkfbm1_div; + period_vco_half = period_vco /2; + pll_locked_delay = period_fb * clkfbm1_div; + clkin_dly_t = period_avg * (clkind_div + 1.25); + clkfb_dly_t = period_fb * 2.25 ; + period_vco1 = period_vco / 8; + period_vco2 = period_vco / 4; + period_vco3 = period_vco * 3/ 8; + period_vco4 = period_vco / 2; + period_vco5 = period_vco * 5 / 8; + period_vco6 = period_vco *3 / 4; + period_vco7 = period_vco * 7 / 8; + md_product = clkind_div * clkfbm1_div; + md_product_dbl = clkind_div * clkfbm1_div * 2; + end + + assign clkvco_lk_rst = ( rst_in == 1 || pll_unlock == 1 || pll_locked_tm == 0) ? 1 : 0; + + always @(clkvco_lk_rst) + if (clkvco_lk_rst) + assign clkvco_lk = 0; + else + deassign clkvco_lk; + + +// always @(posedge clkpll or posedge rst_in or posedge pll_unlock) +// if ( rst_in == 1 || pll_unlock == 1 || pll_locked_tm == 0) begin +// clkvco_lk <= 0; +// end +// else begin + always @(posedge clkpll) + if (pll_locked_tm ==1) begin + clkvco_lk <= 1; + for (i1=1; i1 < md_product_dbl; i1=i1+1) + #(period_vco_half) clkvco_lk <= ~clkvco_lk; + end + + + always @(fb_delay or period_vco or clkfbm1_dly or clkfbm1pm_rl) begin + val_tmp = period_vco * md_product; + fbm1_comp_delay = period_vco *(clkfbm1_dly + clkfbm1pm_rl ); + dly_tmp = fb_delay + fbm1_comp_delay; + if (fb_delay == 0) + clkvco_delay = 0; + else if ( dly_tmp < val_tmp) + clkvco_delay = val_tmp - dly_tmp; + else + clkvco_delay = val_tmp - dly_tmp % val_tmp ; + end + + always @(clkfbm1pm_sel) + case (clkfbm1pm_sel) + 3'b000 : clkfbm1pm_rl = 0.0; + 3'b001 : clkfbm1pm_rl = 0.125; + 3'b010 : clkfbm1pm_rl = 0.25; + 3'b011 : clkfbm1pm_rl = 0.375; + 3'b100 : clkfbm1pm_rl = 0.50; + 3'b101 : clkfbm1pm_rl = 0.625; + 3'b110 : clkfbm1pm_rl = 0.75; + 3'b111 : clkfbm1pm_rl = 0.875; + endcase + + always @(clkvco_free ) + if (pmcd_mode != 1 && pll_locked_tm == 0) + clkvco_free <= #period_vco_target_half ~clkvco_free; + + always @(clkvco_lk or clkvco_free or pll_locked_tm) + if ( pll_locked_tm) + clkvco <= #clkvco_delay clkvco_lk; + else + clkvco <= #clkvco_delay clkvco_free; + + always @(clk0_ht or clk0_lt or clk0_nocnt or init_trig) + clkout_pm_cal(clk0_ht1, clk0_div, clk0_div1, clk0_ht, clk0_lt, clk0_nocnt, clk0_edge); + + always @(clk1_ht or clk1_lt or clk1_nocnt or init_trig) + clkout_pm_cal(clk1_ht1, clk1_div, clk1_div1, clk1_ht, clk1_lt, clk1_nocnt, clk1_edge); + + always @(clk2_ht or clk2_lt or clk2_nocnt or init_trig) + clkout_pm_cal(clk2_ht1, clk2_div, clk2_div1, clk2_ht, clk2_lt, clk2_nocnt, clk2_edge); + + always @(clk3_ht or clk3_lt or clk3_nocnt or init_trig) + clkout_pm_cal(clk3_ht1, clk3_div, clk3_div1, clk3_ht, clk3_lt, clk3_nocnt, clk3_edge); + + always @(clk4_ht or clk4_lt or clk4_nocnt or init_trig) + clkout_pm_cal(clk4_ht1, clk4_div, clk4_div1, clk4_ht, clk4_lt, clk4_nocnt, clk4_edge); + + always @(clk5_ht or clk5_lt or clk5_nocnt or init_trig) + clkout_pm_cal(clk5_ht1, clk5_div, clk5_div1, clk5_ht, clk5_lt, clk5_nocnt, clk5_edge); + + always @(clkfbm1_ht or clkfbm1_lt or clkfbm1_nocnt or init_trig) + clkout_pm_cal(clkfbm1_ht1, clkfbm1_div, clkfbm1_div1, clkfbm1_ht, clkfbm1_lt, clkfbm1_nocnt, clkfbm1_edge); + + always @(rst_in) + if (rst_in) + assign clkout_mux = 8'b0; + else + deassign clkout_mux; + + always @(clkvco or clkout_en ) + if (clkout_en) begin + clkout_mux[0] <= clkvco; + clkout_mux[1] <= #(period_vco1) clkvco; + clkout_mux[2] <= #(period_vco2) clkvco; + clkout_mux[3] <= #(period_vco3) clkvco; + clkout_mux[4] <= #(period_vco4) clkvco; + clkout_mux[5] <= #(period_vco5) clkvco; + clkout_mux[6] <= #(period_vco6) clkvco; + clkout_mux[7] <= #(period_vco7) clkvco; + end + + assign clk0in = clkout_mux[clk0pm_sel]; + assign clk1in = clkout_mux[clk1pm_sel]; + assign clk2in = clkout_mux[clk2pm_sel]; + assign clk3in = clkout_mux[clk3pm_sel]; + assign clk4in = clkout_mux[clk4pm_sel]; + assign clk5in = clkout_mux[clk5pm_sel]; + assign clkfbm1in = clkout_mux[clkfbm1pm_sel]; + + assign clk0ps_en = (clk0_dly_cnt == clkout0_dly) ? clkout_en : 0; + assign clk1ps_en = (clk1_dly_cnt == clkout1_dly) ? clkout_en : 0; + assign clk2ps_en = (clk2_dly_cnt == clkout2_dly) ? clkout_en : 0; + assign clk3ps_en = (clk3_dly_cnt == clkout3_dly) ? clkout_en : 0; + assign clk4ps_en = (clk4_dly_cnt == clkout4_dly) ? clkout_en : 0; + assign clk5ps_en = (clk5_dly_cnt == clkout5_dly) ? clkout_en : 0; + assign clkfbm1ps_en = (clkfbm1_dly_cnt == clkfbm1_dly) ? clkout_en : 0; + + always @(negedge clk0in or posedge rst_in) + if (rst_in) + clk0_dly_cnt <= 6'b0; + else + if (clk0_dly_cnt < clkout0_dly && clkout_en ==1) + clk0_dly_cnt <= clk0_dly_cnt + 1; + + always @(negedge clk1in or posedge rst_in) + if (rst_in) + clk1_dly_cnt <= 6'b0; + else + if (clk1_dly_cnt < clkout1_dly && clkout_en ==1) + clk1_dly_cnt <= clk1_dly_cnt + 1; + + always @(negedge clk2in or posedge rst_in) + if (rst_in) + clk2_dly_cnt <= 6'b0; + else + if (clk2_dly_cnt < clkout2_dly && clkout_en ==1) + clk2_dly_cnt <= clk2_dly_cnt + 1; + + always @(negedge clk3in or posedge rst_in) + if (rst_in) + clk3_dly_cnt <= 6'b0; + else + if (clk3_dly_cnt < clkout3_dly && clkout_en ==1) + clk3_dly_cnt <= clk3_dly_cnt + 1; + + always @(negedge clk4in or posedge rst_in) + if (rst_in) + clk4_dly_cnt <= 6'b0; + else + if (clk4_dly_cnt < clkout4_dly && clkout_en ==1) + clk4_dly_cnt <= clk4_dly_cnt + 1; + + always @(negedge clk5in or posedge rst_in) + if (rst_in) + clk5_dly_cnt <= 6'b0; + else + if (clk5_dly_cnt < clkout5_dly && clkout_en ==1) + clk5_dly_cnt <= clk5_dly_cnt + 1; + + always @(negedge clkfbm1in or posedge rst_in) + if (rst_in) + clkfbm1_dly_cnt <= 6'b0; + else + if (clkfbm1_dly_cnt < clkfbm1_dly && clkout_en ==1) + clkfbm1_dly_cnt <= clkfbm1_dly_cnt + 1; + + always @(posedge clk0in or negedge clk0in or posedge rst_in) + if (rst_in) begin + clk0_cnt <= 8'b0; + clk0_out <= 0; + end + else if (clk0ps_en) begin + if (clk0_cnt < clk0_div1) + clk0_cnt <= clk0_cnt + 1; + else + clk0_cnt <= 8'b0; + + if (clk0_cnt < clk0_ht1) + clk0_out <= 1; + else + clk0_out <= 0; + end + else begin + clk0_cnt <= 8'b0; + clk0_out <= 0; + end + + always @(posedge clk1in or negedge clk1in or posedge rst_in) + if (rst_in) begin + clk1_cnt <= 8'b0; + clk1_out <= 0; + end + else if (clk1ps_en) begin + if (clk1_cnt < clk1_div1) + clk1_cnt <= clk1_cnt + 1; + else + clk1_cnt <= 8'b0; + + if (clk1_cnt < clk1_ht1) + clk1_out <= 1; + else + clk1_out <= 0; + end + else begin + clk1_cnt <= 8'b0; + clk1_out <= 0; + end + + always @(posedge clk2in or negedge clk2in or posedge rst_in) + if (rst_in) begin + clk2_cnt <= 8'b0; + clk2_out <= 0; + end + else if (clk2ps_en) begin + if (clk2_cnt < clk2_div1) + clk2_cnt <= clk2_cnt + 1; + else + clk2_cnt <= 8'b0; + + if (clk2_cnt < clk2_ht1) + clk2_out <= 1; + else + clk2_out <= 0; + end + else begin + clk2_cnt <= 8'b0; + clk2_out <= 0; + end + + always @(posedge clk3in or negedge clk3in or posedge rst_in) + if (rst_in) begin + clk3_cnt <= 8'b0; + clk3_out <= 0; + end + else if (clk3ps_en) begin + if (clk3_cnt < clk3_div1) + clk3_cnt <= clk3_cnt + 1; + else + clk3_cnt <= 8'b0; + + if (clk3_cnt < clk3_ht1) + clk3_out <= 1; + else + clk3_out <= 0; + end + else begin + clk3_cnt <= 8'b0; + clk3_out <= 0; + end + + + always @(posedge clk4in or negedge clk4in or posedge rst_in) + if (rst_in) begin + clk4_cnt <= 8'b0; + clk4_out <= 0; + end + else if (clk4ps_en) begin + if (clk4_cnt < clk4_div1) + clk4_cnt <= clk4_cnt + 1; + else + clk4_cnt <= 8'b0; + + if (clk4_cnt < clk4_ht1) + clk4_out <= 1; + else + clk4_out <= 0; + end + else begin + clk4_cnt <= 8'b0; + clk4_out <= 0; + end + + + always @(posedge clk5in or negedge clk5in or posedge rst_in) + if (rst_in) begin + clk5_cnt <= 8'b0; + clk5_out <= 0; + end + else if (clk5ps_en) begin + if (clk5_cnt < clk5_div1) + clk5_cnt <= clk5_cnt + 1; + else + clk5_cnt <= 8'b0; + + if (clk5_cnt < clk5_ht1) + clk5_out <= 1; + else + clk5_out <= 0; + end + else begin + clk5_cnt <= 8'b0; + clk5_out <= 0; + end + + + always @(posedge clkfbm1in or negedge clkfbm1in or posedge rst_in) + if (rst_in) begin + clkfbm1_cnt <= 8'b0; + clkfbm1_out <= 0; + end + else if (clkfbm1ps_en) begin + if (clkfbm1_cnt < clkfbm1_div1) + clkfbm1_cnt <= clkfbm1_cnt + 1; + else + clkfbm1_cnt <= 8'b0; + + if (clkfbm1_cnt < clkfbm1_ht1) + clkfbm1_out <= 1; + else + clkfbm1_out <= 0; + end + else begin + clkfbm1_cnt <= 8'b0; + clkfbm1_out <= 0; + end + + + + always @(clk0_out or clkfb_tst or fb_delay_found) + if (fb_delay_found == 1) + clkout0_out = clk0_out; + else + clkout0_out = clkfb_tst; + + always @(clk1_out or clkfb_tst or fb_delay_found) + if (fb_delay_found == 1) + clkout1_out = clk1_out; + else + clkout1_out = clkfb_tst; + + always @(clk2_out or clkfb_tst or fb_delay_found) + if (fb_delay_found == 1) + clkout2_out = clk2_out; + else + clkout2_out = clkfb_tst; + + always @(clk3_out or clkfb_tst or fb_delay_found) + if (fb_delay_found == 1) + clkout3_out = clk3_out; + else + clkout3_out = clkfb_tst; + + always @(clk4_out or clkfb_tst or fb_delay_found) + if (fb_delay_found == 1) + clkout4_out = clk4_out; + else + clkout4_out = clkfb_tst; + + always @(clk5_out or clkfb_tst or fb_delay_found) + if (fb_delay_found == 1) + clkout5_out = clk5_out; + else + clkout5_out = clkfb_tst; + + always @(clkfbm1_out or clkfb_tst or fb_delay_found) + if (fb_delay_found == 1) + clkfb_out = clkfbm1_out; + else + clkfb_out = clkfb_tst; + +// +// determine feedback delay +// + +always @(rst_in1) + if (rst_in1) + assign clkfb_tst = 0; + else + deassign clkfb_tst; + +always @(posedge clkpll ) + if (fb_delay_found_tmp == 0 && GSR == 0 && rst_in1 == 0) begin + clkfb_tst <= 1'b1; + end + else + clkfb_tst <= 1'b0; + + +always @( posedge clkfb_tst or posedge rst_in1 ) + if (rst_in1) + delay_edge <= 0; + else + delay_edge <= $time; + +always @(posedge clkfb_in or posedge rst_in1 ) + if (rst_in1) begin + fb_delay <= 0; + fb_delay_found_tmp <= 0; + end + else + if (fb_delay_found_tmp ==0 ) begin + if ( delay_edge != 0) + fb_delay <= ($time - delay_edge); + else + fb_delay <= 0; + fb_delay_found_tmp <= 1; + end + +always @(rst_in1) + if (rst_in1) + assign fb_delay_found = 0; + else + deassign fb_delay_found; + +always @(fb_delay_found_tmp or clkvco_delay ) + fb_delay_found <= #(clkvco_delay) fb_delay_found_tmp; + + +always @(fb_delay) + if (rst_in1==0 && (fb_delay/1000.0 > fb_delay_max)) begin + $display("Warning : The feedback delay on PLL_ADV instance %m at time %t is %f ns. It is over the maximun value %f ns.", $time, fb_delay / 1000.0, fb_delay_max); + end + +// +// generate unlock signal +// + +always @(clkpll) + clkpll_dly <= #clkin_dly_t clkpll; + +always @(clkfb_in) + if (pmcd_mode != 1) + clkfbin_dly <= #clkfb_dly_t clkfb_in; + else + clkfbin_dly = 0; + +always @( posedge clkpll_dly or negedge clkpll or posedge rst_in) + if (rst_in || clkpll == 0) begin + clkstop_cnt_p = 0; + clkin_stopped_p = 0; + end + else + if (fb_delay_found && pll_locked_tmp2) begin + if (clkpll && clkpll_jitter_unlock == 0) + clkstop_cnt_p <= clkstop_cnt_p +1; + else + clkstop_cnt_p = 0; + + if (clkstop_cnt_p > clkin_stop_max) + clkin_stopped_p <= 1; + else + clkin_stopped_p = 0; + end + else begin + clkstop_cnt_p = 0; + clkin_stopped_p = 0; + end + +always @( posedge clkpll_dly or posedge clkpll or posedge rst_in) + if (rst_in || clkpll == 1) begin + clkstop_cnt_n = 0; + clkin_stopped_n = 0; + end + else + if (fb_delay_found && pll_locked_tmp2) begin + if (clkpll==0 && clkpll_jitter_unlock == 0) + clkstop_cnt_n <= clkstop_cnt_n +1; + else + clkstop_cnt_n = 0; + + if (clkstop_cnt_n > clkin_stop_max) + clkin_stopped_n <= 1; + else + clkin_stopped_n = 0; + end + else begin + clkstop_cnt_n = 0; + clkin_stopped_n = 0; + end + + +always @( posedge clkfbin_dly or negedge clkfb_in or posedge rst_in) + if (rst_in || clkfb_in == 0) begin + clkfbstop_cnt_p = 0; + clkfb_stopped_p = 0; + end + else + if (fb_delay_found && pll_locked_tmp2) begin + if (clkfb_in && clkpll_jitter_unlock == 0) + clkfbstop_cnt_p <= clkfbstop_cnt_p +1; + else + clkfbstop_cnt_p = 0; + + if (clkfbstop_cnt_p > clkfb_stop_max) + clkfb_stopped_p <= 1; + else + clkfb_stopped_p = 0; + end + else begin + clkfbstop_cnt_p = 0; + clkfb_stopped_p = 0; + end + +always @( posedge clkfbin_dly or posedge clkfb_in or posedge rst_in) + if (rst_in==1 || clkfb_in == 1) begin + clkfbstop_cnt_n = 0; + clkfb_stopped_n = 0; + end + else + if (fb_delay_found && pll_locked_tmp2) begin + if (clkfb_in==0 && clkpll_jitter_unlock == 0) + clkfbstop_cnt_n <= clkfbstop_cnt_n +1; + else + clkfbstop_cnt_n = 0; + + if (clkfbstop_cnt_n > clkfb_stop_max) + clkfb_stopped_n <= 1; + else + clkfb_stopped_n = 0; + end + else begin + clkfbstop_cnt_n = 0; + clkfb_stopped_n = 0; + end + +always @(clkin_jit or rst_in ) + if (rst_in) + clkpll_jitter_unlock = 0; + else + if ( pll_locked_tmp2 && clkfb_stopped == 0 && clkin_stopped == 0) begin + if ((clkin_jit > REF_CLK_JITTER_MAX_tmp) || (clkin_jit < -REF_CLK_JITTER_MAX_tmp)) + clkpll_jitter_unlock = 1; + else + clkpll_jitter_unlock = 0; + end + else + clkpll_jitter_unlock = 0; + + assign clkin_stopped = (clkin_stopped_p || clkin_stopped_n) ? 1 : 0; + assign clkfb_stopped = (clkfb_stopped_p ||clkfb_stopped_n) ? 1 : 0; + assign pll_unlock = (clkin_stopped || clkfb_stopped || clkpll_jitter_unlock) ? 1 : 0; + +// tasks + + +task clkout_dly_cal; +output [5:0] clkout_dly; +output [2:0] clkpm_sel; +input clkdiv; +input clk_ps; +input reg [160:0] clk_ps_name; + +integer clkdiv; +real clk_ps; +real clk_ps_rl; + +real clk_dly_rl, clk_dly_rem; +integer clkout_dly_tmp; + +begin + + if (clk_ps < 0.0) + clk_dly_rl = (360.0 + clk_ps) * clkdiv / 360.0; + else + clk_dly_rl = clk_ps * clkdiv / 360.0; + + clkout_dly_tmp = $rtoi(clk_dly_rl); + + if (clkout_dly_tmp > 63) begin + $display(" Warning : Attribute %s of PLL_ADV on instance %m is set to %f. Required phase shifting can not be reached since it is over the maximum phase shifting ability of X_PLL_ADV", clk_ps_name, clk_ps); + clkout_dly = 6'b111111; + end + else + clkout_dly = clkout_dly_tmp; + + clk_dly_rem = clk_dly_rl - clkout_dly; + + if (clk_dly_rem < 0.125) + clkpm_sel = 0; + else if (clk_dly_rem >= 0.125 && clk_dly_rem < 0.25) + clkpm_sel = 1; + else if (clk_dly_rem >= 0.25 && clk_dly_rem < 0.375) + clkpm_sel = 2; + else if (clk_dly_rem >= 0.375 && clk_dly_rem < 0.5) + clkpm_sel = 3; + else if (clk_dly_rem >= 0.5 && clk_dly_rem < 0.625) + clkpm_sel = 4; + else if (clk_dly_rem >= 0.625 && clk_dly_rem < 0.75) + clkpm_sel = 5; + else if (clk_dly_rem >= 0.75 && clk_dly_rem < 0.875) + clkpm_sel = 6; + else if (clk_dly_rem >= 0.875 ) + clkpm_sel = 7; + + if (clk_ps < 0.0) + clk_ps_rl = (clkout_dly + 0.125 * clkpm_sel)* 360.0 / clkdiv - 360.0; + else + clk_ps_rl = (clkout_dly + 0.125 * clkpm_sel) * 360.0 / clkdiv; + + if (((clk_ps_rl- clk_ps) > 0.001) || ((clk_ps_rl- clk_ps) < -0.001)) + $display(" Warning : Attribute %s of PLL_ADV on instance %m is set to %f. Real phase shifting is %f. Required phase shifting can not be reached.", clk_ps_name, clk_ps, clk_ps_rl); + +end +endtask + + +task clk_out_para_cal; +output [6:0] clk_ht; +output [6:0] clk_lt; +output clk_nocnt; +output clk_edge; +input CLKOUT_DIVIDE; +input CLKOUT_DUTY_CYCLE; + +integer CLKOUT_DIVIDE; +real CLKOUT_DUTY_CYCLE; + +real tmp_value; +integer tmp_value1; +real tmp_value2; + +begin + tmp_value = CLKOUT_DIVIDE * CLKOUT_DUTY_CYCLE; + tmp_value1 = $rtoi(tmp_value * 2) % 2; + tmp_value2 = CLKOUT_DIVIDE - tmp_value; + + + if ((tmp_value) >= O_MAX_HT_LT) begin +// clk_ht = O_MAX_HT_LT; + clk_ht = 7'b1000000; + end + else begin + if (tmp_value < 1.0) + clk_ht = 1; + else + if ( tmp_value1 != 0) + clk_ht = $rtoi(tmp_value) + 1; + else + clk_ht = $rtoi(tmp_value); + end + + if ( (CLKOUT_DIVIDE - clk_ht) >= O_MAX_HT_LT) + clk_lt = 7'b1000000; + else + clk_lt = CLKOUT_DIVIDE - clk_ht; + + clk_nocnt = (CLKOUT_DIVIDE ==1) ? 1 : 0; + if ( tmp_value < 1.0) + clk_edge = 1; + else if (tmp_value1 != 0) + clk_edge = 1; + else + clk_edge = 0; +end +endtask + + +function clkout_duty_chk; + input CLKOUT_DIVIDE; + input CLKOUT_DUTY_CYCLE; + input reg [160:0] CLKOUT_DUTY_CYCLE_N; + + integer CLKOUT_DIVIDE, step_tmp; + real CLKOUT_DUTY_CYCLE; + + real CLK_DUTY_CYCLE_MIN, CLK_DUTY_CYCLE_MAX, CLK_DUTY_CYCLE_STEP; + real CLK_DUTY_CYCLE_MIN_rnd; + reg clk_duty_tmp_int; + +begin + + if (CLKOUT_DIVIDE > O_MAX_HT_LT) begin + CLK_DUTY_CYCLE_MIN = (CLKOUT_DIVIDE - O_MAX_HT_LT)/CLKOUT_DIVIDE; + CLK_DUTY_CYCLE_MAX = (O_MAX_HT_LT + 0.5)/CLKOUT_DIVIDE; + CLK_DUTY_CYCLE_MIN_rnd = CLK_DUTY_CYCLE_MIN; + end + else begin + if (CLKOUT_DIVIDE == 1) begin + CLK_DUTY_CYCLE_MIN = 0.0; + CLK_DUTY_CYCLE_MIN_rnd = 0.0; + end + else begin + step_tmp = 1000 / CLKOUT_DIVIDE; + CLK_DUTY_CYCLE_MIN_rnd = step_tmp / 1000.0; + CLK_DUTY_CYCLE_MIN = 1.0 /CLKOUT_DIVIDE; + end + CLK_DUTY_CYCLE_MAX = 1.0; + end + + if (CLKOUT_DUTY_CYCLE > CLK_DUTY_CYCLE_MAX || CLKOUT_DUTY_CYCLE < CLK_DUTY_CYCLE_MIN_rnd) begin + $display(" Attribute Syntax Warning : %s is set to %f on instance %m and is not in the allowed range %f to %f.", CLKOUT_DUTY_CYCLE_N, CLKOUT_DUTY_CYCLE, CLK_DUTY_CYCLE_MIN, CLK_DUTY_CYCLE_MAX ); + end + + clk_duty_tmp_int = 0; + CLK_DUTY_CYCLE_STEP = 0.5 / CLKOUT_DIVIDE; + for (j = 0; j < (2 * CLKOUT_DIVIDE - CLK_DUTY_CYCLE_MIN/CLK_DUTY_CYCLE_STEP); j = j + 1) + if (((CLK_DUTY_CYCLE_MIN + CLK_DUTY_CYCLE_STEP * j) - CLKOUT_DUTY_CYCLE) > -0.001 && + ((CLK_DUTY_CYCLE_MIN + CLK_DUTY_CYCLE_STEP * j) - CLKOUT_DUTY_CYCLE) < 0.001) + clk_duty_tmp_int = 1; + + if ( clk_duty_tmp_int != 1) begin + $display(" Attribute Syntax Warning : %s is set to %f on instance %m and is not an allowed value. Allowed values are:", CLKOUT_DUTY_CYCLE_N, CLKOUT_DUTY_CYCLE); + for (j = 0; j < (2 * CLKOUT_DIVIDE - CLK_DUTY_CYCLE_MIN/CLK_DUTY_CYCLE_STEP); j = j + 1) + $display("%f", CLK_DUTY_CYCLE_MIN + CLK_DUTY_CYCLE_STEP * j); + end + + clkout_duty_chk = 1'b1; +end +endfunction + + +function para_int_pmcd_chk; + input para_in; + input reg [160:0] para_name; + input range_low; + input range_high; + input pmcd_mode; + input pmcd_value; + + integer para_in; + integer range_low; + integer range_high; + integer pmcd_value; +begin + + if (para_in < range_low || para_in > range_high) + begin + $display("Attribute Syntax Error : The Attribute %s on PLL_ADV instance %m is set to %d. Legal values for this attribute are %d to %d.", para_name, para_in, range_low, range_high); + $finish; + end + else if (pmcd_mode == 1 && para_in != pmcd_value) begin + $display("Attribute Syntax Error : The Attribute %s on PLL_ADV instance %m is set to %d when attribute PLL_PMCD_MODE is set to TRUE. Legal values for this attribute is %d when PLL in PMCD MODE.", para_name, para_in, pmcd_value); + $finish; + end + + para_int_pmcd_chk = 1'b1; +end +endfunction + +function para_real_pmcd_chk; + input para_in; + input reg [160:0] para_name; + input range_low; + input range_high; + input pmcd_mode; + input pmcd_value; + + real para_in; + real range_low; + real range_high; + real pmcd_value; +begin + + if (para_in < range_low || para_in > range_high) + begin + $display("Attribute Syntax Error : The Attribute %s on PLL_ADV instance %m is set to %f. Legal values for this attribute are %f to %f.", para_name, para_in, range_low, range_high); + $finish; + end + else if (pmcd_mode == 1 && para_in != pmcd_value) begin + $display("Attribute Syntax Error : The Attribute %s on PLL_ADV instance %m is set to %f when attribute PLL_PMCD_MODE is set to TRUE. Legal values for this attribute is %f when PLL in PMCD MODE.", para_name, para_in, pmcd_value); + $finish; + end + + para_real_pmcd_chk = 1'b0; +end +endfunction + +function para_int_range_chk; + input para_in; + input reg [160:0] para_name; + input range_low; + input range_high; + + integer para_in; + integer range_low; + integer range_high; +begin + if ( para_in < range_low || para_in > range_high) begin + $display("Attribute Syntax Error : The Attribute %s on PLL_ADV instance %m is set to %d. Legal values for this attribute are %d to %d.", para_name, para_in, range_low, range_high); + $finish; + end + para_int_range_chk = 1'b1; +end +endfunction + +function para_real_range_chk; + input para_in; + input reg [160:0] para_name; + input range_low; + input range_high; + + real para_in; + real range_low; + real range_high; +begin + if ( para_in < range_low || para_in > range_high) begin + $display("Attribute Syntax Error : The Attribute %s on PLL_ADV instance %m is set to %f. Legal values for this attribute are %f to %f.", para_name, para_in, range_low, range_high); + $finish; + end + + para_real_range_chk = 1'b0; +end +endfunction + +task clkout_pm_cal; + output [7:0] clk_ht1; + output [7:0] clk_div; + output [7:0] clk_div1; + input [6:0] clk_ht; + input [6:0] clk_lt; + input clk_nocnt; + input clk_edge; + +begin + if (clk_nocnt ==1) begin + clk_div = 8'b00000001; + clk_div1 = 8'b00000001; + clk_ht1 = 8'b00000001; + end + else begin + if ( clk_edge == 1) + clk_ht1 = 2 * clk_ht -1; + else + clk_ht1 = 2 * clk_ht; + clk_div = clk_ht + clk_lt ; + clk_div1 = 2 * clk_div -1; + end +end +endtask + +task clkout_delay_para_drp; + output [5:0] clkout_dly; + output clk_nocnt; + output clk_edge; + input [15:0] di_in; + input [4:0] daddr_in; +begin + +// if (di_in[15:8] != 8'h00) begin +// $display(" Error : PLL_ADV on instance %m input DI[15:8] is set to %h and need to be set to 00h at address DADDR=%b at time %t.", di_in[15:8], daddr_in, $time); +// $finish; +// end + clkout_dly = di_in[5:0]; + clk_nocnt = di_in[6]; + clk_edge = di_in[7]; +end +endtask + +task clkout_hl_para_drp; + output [6:0] clk_lt; + output [6:0] clk_ht; + output [2:0] clkpm_sel; + input [15:0] di_in_tmp; + input [4:0] daddr_in_tmp; +begin + if (di_in_tmp[12] != 1) begin + $display(" Error : PLL_ADV on instance %m input DI is %h at address DADDR=%b at time %t. The bit 12 need to be set to 1 .", di_in_tmp, daddr_in_tmp, $time); +// $finish; + end + if ( di_in_tmp[5:0] == 6'b0) + clk_lt = 7'b1000000; + else + clk_lt = { 1'b0, di_in[5:0]}; + if (di_in_tmp[11:6] == 6'b0) + clk_ht = 7'b1000000; + else + clk_ht = { 1'b0, di_in_tmp[11:6]}; + clkpm_sel = di_in_tmp[15:13]; +end +endtask + + + +endmodule diff --git a/fpga/usrp2/models/PLL_BASE.v b/fpga/usrp2/models/PLL_BASE.v new file mode 100644 index 000000000..f2180a25a --- /dev/null +++ b/fpga/usrp2/models/PLL_BASE.v @@ -0,0 +1,150 @@ +// $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/rainier/PLL_BASE.v,v 1.5 2006/03/13 21:53:44 yanx Exp $ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 1995/2004 Xilinx, Inc. +// All Right Reserved. +/////////////////////////////////////////////////////////////////////////////// +// ____ ____ +// / /\/ / +// /___/ \ / Vendor : Xilinx +// \ \ \/ Version : 10.1 +// \ \ Description : Xilinx Timing Simulation Library Component +// / / Phase Lock Loop Clock +// /___/ /\ Filename : PLL_BASE.v +// \ \ / \ Timestamp : +// \___\/\___\ +// +// Revision: +// 12/02/05 - Initial version. +// 02/24/06 - Add real/integer to parameter. +// End Revision + + +`timescale 1 ps / 1 ps + +module PLL_BASE ( + CLKFBOUT, + CLKOUT0, + CLKOUT1, + CLKOUT2, + CLKOUT3, + CLKOUT4, + CLKOUT5, + LOCKED, + CLKFBIN, + CLKIN, + RST + +); + +parameter BANDWIDTH = "OPTIMIZED"; +parameter integer CLKFBOUT_MULT = 1; +parameter real CLKFBOUT_PHASE = 0.0; +parameter real CLKIN_PERIOD = 0.000; +parameter integer CLKOUT0_DIVIDE = 1; +parameter real CLKOUT0_DUTY_CYCLE = 0.5; +parameter real CLKOUT0_PHASE = 0.0; +parameter integer CLKOUT1_DIVIDE = 1; +parameter real CLKOUT1_DUTY_CYCLE = 0.5; +parameter real CLKOUT1_PHASE = 0.0; +parameter integer CLKOUT2_DIVIDE = 1; +parameter real CLKOUT2_DUTY_CYCLE = 0.5; +parameter real CLKOUT2_PHASE = 0.0; +parameter integer CLKOUT3_DIVIDE = 1; +parameter real CLKOUT3_DUTY_CYCLE = 0.5; +parameter real CLKOUT3_PHASE = 0.0; +parameter integer CLKOUT4_DIVIDE = 1; +parameter real CLKOUT4_DUTY_CYCLE = 0.5; +parameter real CLKOUT4_PHASE = 0.0; +parameter integer CLKOUT5_DIVIDE = 1; +parameter real CLKOUT5_DUTY_CYCLE = 0.5; +parameter real CLKOUT5_PHASE = 0.0; +parameter COMPENSATION = "SYSTEM_SYNCHRONOUS"; +parameter integer DIVCLK_DIVIDE = 1; +parameter real REF_JITTER = 0.100; +parameter RESET_ON_LOSS_OF_LOCK = "FALSE"; + + +output CLKFBOUT; +output CLKOUT0; +output CLKOUT1; +output CLKOUT2; +output CLKOUT3; +output CLKOUT4; +output CLKOUT5; +output LOCKED; + +input CLKFBIN; +input CLKIN; +input RST; + + +wire OPEN_CLKFBDCM; +wire OPEN_CLKOUTDCM0; +wire OPEN_CLKOUTDCM1; +wire OPEN_CLKOUTDCM2; +wire OPEN_CLKOUTDCM3; +wire OPEN_CLKOUTDCM4; +wire OPEN_CLKOUTDCM5; +wire OPEN_DRDY; +wire [15:0] OPEN_DO; + +PLL_ADV pll_adv_1 ( + .CLKFBDCM (OPEN_CLKFBDCM), + .CLKFBIN (CLKFBIN), + .CLKFBOUT (CLKFBOUT), + .CLKIN1 (CLKIN), + .CLKIN2 (1'b0), + .CLKOUT0 (CLKOUT0), + .CLKOUT1 (CLKOUT1), + .CLKOUT2 (CLKOUT2), + .CLKOUT3 (CLKOUT3), + .CLKOUT4 (CLKOUT4), + .CLKOUT5 (CLKOUT5), + .CLKOUTDCM0 (OPEN_CLKOUTDCM0), + .CLKOUTDCM1 (OPEN_CLKOUTDCM1), + .CLKOUTDCM2 (OPEN_CLKOUTDCM2), + .CLKOUTDCM3 (OPEN_CLKOUTDCM3), + .CLKOUTDCM4 (OPEN_CLKOUTDCM4), + .CLKOUTDCM5 (OPEN_CLKOUTDCM5), + .DADDR (5'b0), + .DCLK (1'b0), + .DEN (1'b0), + .DI (16'b0), + .DO (OPEN_DO), + .DRDY (OPEN_DRDY), + .DWE (1'b0), + .LOCKED (LOCKED), + .CLKINSEL(1'b1), + .REL (1'b0), + .RST (RST) +); + +defparam pll_adv_1.BANDWIDTH = BANDWIDTH; +defparam pll_adv_1.CLKFBOUT_MULT = CLKFBOUT_MULT; +defparam pll_adv_1.CLKFBOUT_PHASE = CLKFBOUT_PHASE; +defparam pll_adv_1.CLKIN1_PERIOD = CLKIN_PERIOD; +defparam pll_adv_1.CLKIN2_PERIOD = 10.0; +defparam pll_adv_1.CLKOUT0_DIVIDE = CLKOUT0_DIVIDE; +defparam pll_adv_1.CLKOUT0_DUTY_CYCLE = CLKOUT0_DUTY_CYCLE; +defparam pll_adv_1.CLKOUT0_PHASE = CLKOUT0_PHASE; +defparam pll_adv_1.CLKOUT1_DIVIDE = CLKOUT1_DIVIDE; +defparam pll_adv_1.CLKOUT1_DUTY_CYCLE = CLKOUT1_DUTY_CYCLE; +defparam pll_adv_1.CLKOUT1_PHASE = CLKOUT1_PHASE; +defparam pll_adv_1.CLKOUT2_DIVIDE = CLKOUT2_DIVIDE; +defparam pll_adv_1.CLKOUT2_DUTY_CYCLE = CLKOUT2_DUTY_CYCLE; +defparam pll_adv_1.CLKOUT2_PHASE = CLKOUT2_PHASE; +defparam pll_adv_1.CLKOUT3_DIVIDE = CLKOUT3_DIVIDE; +defparam pll_adv_1.CLKOUT3_DUTY_CYCLE = CLKOUT3_DUTY_CYCLE; +defparam pll_adv_1.CLKOUT3_PHASE = CLKOUT3_PHASE; +defparam pll_adv_1.CLKOUT4_DIVIDE = CLKOUT4_DIVIDE; +defparam pll_adv_1.CLKOUT4_DUTY_CYCLE = CLKOUT4_DUTY_CYCLE; +defparam pll_adv_1.CLKOUT4_PHASE = CLKOUT4_PHASE; +defparam pll_adv_1.CLKOUT5_DIVIDE = CLKOUT5_DIVIDE; +defparam pll_adv_1.CLKOUT5_DUTY_CYCLE = CLKOUT5_DUTY_CYCLE; +defparam pll_adv_1.CLKOUT5_PHASE = CLKOUT5_PHASE; +defparam pll_adv_1.COMPENSATION = COMPENSATION; +defparam pll_adv_1.DIVCLK_DIVIDE = DIVCLK_DIVIDE; +defparam pll_adv_1.REF_JITTER = REF_JITTER; +defparam pll_adv_1.RESET_ON_LOSS_OF_LOCK = RESET_ON_LOSS_OF_LOCK; + +endmodule diff --git a/fpga/usrp2/top/B100/B100.v b/fpga/usrp2/top/B100/B100.v index dcda974b4..59bed6066 100644 --- a/fpga/usrp2/top/B100/B100.v +++ b/fpga/usrp2/top/B100/B100.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -50,6 +50,8 @@ module B100 // ///////////////////////////////////////////////////////////////////////// // Clocking wire clk_fpga, clk_fpga_in, reset; + wire gpif_clk = IFCLK; + wire gpif_rst; IBUFGDS #(.IOSTANDARD("LVDS_33"), .DIFF_TERM("TRUE")) clk_fpga_pin (.O(clk_fpga_in),.I(CLK_FPGA_P),.IB(CLK_FPGA_N)); @@ -57,6 +59,7 @@ module B100 BUFG clk_fpga_BUFG (.I(clk_fpga_in), .O(clk_fpga)); reset_sync reset_sync(.clk(clk_fpga), .reset_in((~reset_n) | (~ext_reset)), .reset_out(reset)); + reset_sync reset_sync_gpif(.clk(gpif_clk), .reset_in((~reset_n) | (~ext_reset)), .reset_out(gpif_rst)); // ///////////////////////////////////////////////////////////////////////// // SPI @@ -105,6 +108,7 @@ module B100 // ///////////////////////////////////////////////////////////////////////// // RX ADC -- handles deinterleaving + wire rxsync_0, rxsync_1; reg [11:0] rx_i, rx_q; wire [11:0] rx_a, rx_b; @@ -151,24 +155,64 @@ module B100 rx_i <= rx_a; rx_q <= rx_b; end - + // ///////////////////////////////////////////////////////////////////////// - // Main U1E Core - u1plus_core u1p_c(.clk_fpga(clk_fpga), .rst_fpga(reset), - .debug_led(debug_led), .debug(debug), .debug_clk(debug_clk), - .debug_txd(), .debug_rxd(1'b1), - - .gpif_d(GPIF_D), .gpif_ctl(GPIF_CTL), .gpif_pktend(GPIF_PKTEND), - .gpif_sloe(GPIF_SLOE), .gpif_slwr(GPIF_SLWR), .gpif_slrd(GPIF_SLRD), - .gpif_fifoadr(GPIF_ADR), .gpif_clk(IFCLK), - - .db_sda(SDA_FPGA), .db_scl(SCL_FPGA), - .sclk(sclk), .sen({SEN_CODEC,SEN_TX_DB,SEN_RX_DB}), .mosi(mosi), .miso(miso), - .cgen_st_status(cgen_st_status), .cgen_st_ld(cgen_st_ld),.cgen_st_refmon(cgen_st_refmon), - .cgen_sync_b(cgen_sync_b), .cgen_ref_sel(cgen_ref_sel), - .io_tx(io_tx), .io_rx(io_rx), - .tx_i(tx_i), .tx_q(tx_q), - .rx_i(rx_i), .rx_q(rx_q), - .pps_in(PPS_IN) ); + // Main Core + wire [35:0] rx_data, tx_data, ctrl_data, resp_data; + wire rx_src_rdy, rx_dst_rdy, tx_src_rdy, tx_dst_rdy, resp_src_rdy, resp_dst_rdy, ctrl_src_rdy, ctrl_dst_rdy; + wire dsp_rx_run, dsp_tx_run; + wire [7:0] sen8; + assign {SEN_CODEC,SEN_TX_DB,SEN_RX_DB} = sen8[2:0]; + wire [31:0] core_debug; + + assign debug_led = {dsp_tx_run, dsp_rx_run, cgen_st_ld}; + wire cgen_sync; + assign { cgen_sync_b, cgen_ref_sel } = {~cgen_sync, 1'b1}; + + u1plus_core #( +`ifdef NUM_RX_DSP + .NUM_RX_DSPS(`NUM_RX_DSP), +`else + .NUM_RX_DSPS(1), +`endif + .DSP_RX_XTRA_FIFOSIZE(11), + .DSP_TX_XTRA_FIFOSIZE(12), + .USE_PACKET_PADDER(1) + ) core( + .clk(clk_fpga), .reset(reset), + .debug(core_debug), .debug_clk(debug_clk), + + .rx_data(rx_data), .rx_src_rdy(rx_src_rdy), .rx_dst_rdy(rx_dst_rdy), + .tx_data(tx_data), .tx_src_rdy(tx_src_rdy), .tx_dst_rdy(tx_dst_rdy), + .ctrl_data(ctrl_data), .ctrl_src_rdy(ctrl_src_rdy), .ctrl_dst_rdy(ctrl_dst_rdy), + .resp_data(resp_data), .resp_src_rdy(resp_src_rdy), .resp_dst_rdy(resp_dst_rdy), + + .dsp_rx_run(dsp_rx_run), .dsp_tx_run(dsp_tx_run), + .clock_sync(cgen_sync), + + .db_sda(SDA_FPGA), .db_scl(SCL_FPGA), + .sclk(sclk), .sen(sen8), .mosi(mosi), .miso(miso), + .io_tx(io_tx), .io_rx(io_rx), + .tx_i(tx_i), .tx_q(tx_q), + .rx_i(rx_i), .rx_q(rx_q), + .pps_in(PPS_IN) ); + + // ///////////////////////////////////////////////////////////////////////// + // Interface from host to/from GPIF + wire [31:0] gpif_debug; + slave_fifo slave_fifo (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), .gpif_d(GPIF_D), + .gpif_ctl(GPIF_CTL), .sloe(GPIF_SLOE), .slwr(GPIF_SLWR), .slrd(GPIF_SLRD), + .pktend(GPIF_PKTEND), .fifoadr(GPIF_ADR), + + .fifo_clk(clk_fpga), .fifo_rst(reset), + .rx_data(rx_data), .rx_src_rdy(rx_src_rdy), .rx_dst_rdy(rx_dst_rdy), + .tx_data(tx_data), .tx_src_rdy(tx_src_rdy), .tx_dst_rdy(tx_dst_rdy), + .ctrl_data(ctrl_data), .ctrl_src_rdy(ctrl_src_rdy), .ctrl_dst_rdy(ctrl_dst_rdy), + .resp_data(resp_data), .resp_src_rdy(resp_src_rdy), .resp_dst_rdy(resp_dst_rdy), + + .debug(gpif_debug)); + + //assign debug = gpif_debug; + assign debug = core_debug; endmodule // B100 diff --git a/fpga/usrp2/top/B100/Makefile b/fpga/usrp2/top/B100/Makefile index 3ddef1024..fdd507394 100644 --- a/fpga/usrp2/top/B100/Makefile +++ b/fpga/usrp2/top/B100/Makefile @@ -2,7 +2,7 @@ # Copyright 2011 Ettus Research LLC # -all: B100 +all: B100 B100_2RX find -name "*.twr" | xargs grep constraint | grep met clean: @@ -11,4 +11,7 @@ clean: B100: make -f Makefile.$@ bin +B100_2RX: + make -f Makefile.$@ bin + .PHONY: all clean diff --git a/fpga/usrp2/top/B100/Makefile.B100_2RX b/fpga/usrp2/top/B100/Makefile.B100_2RX new file mode 100644 index 000000000..ba535dfb0 --- /dev/null +++ b/fpga/usrp2/top/B100/Makefile.B100_2RX @@ -0,0 +1,106 @@ +# +# Copyright 2008-2012 Ettus Research LLC +# + +################################################## +# Project Setup +################################################## +TOP_MODULE := B100 +BUILD_DIR := build-B100_2RX/ + +# set me in a custom makefile +CUSTOM_SRCS = +CUSTOM_DEFS = + +################################################## +# Include other makefiles +################################################## + +include ../Makefile.common +include ../../fifo/Makefile.srcs +include ../../control_lib/Makefile.srcs +include ../../sdr_lib/Makefile.srcs +include ../../serdes/Makefile.srcs +include ../../simple_gemac/Makefile.srcs +include ../../timing/Makefile.srcs +include ../../opencores/Makefile.srcs +include ../../vrt/Makefile.srcs +include ../../udp/Makefile.srcs +include ../../coregen/Makefile.srcs +include ../../gpif/Makefile.srcs + +################################################## +# Project Properties +################################################## +export PROJECT_PROPERTIES := \ +family "Spartan3A" \ +device XC3S1400A \ +package ft256 \ +speed -4 \ +top_level_module_type "HDL" \ +synthesis_tool "XST (VHDL/Verilog)" \ +simulator "ISE Simulator (VHDL/Verilog)" \ +"Preferred Language" "Verilog" \ +"Enable Message Filtering" FALSE \ +"Display Incremental Messages" FALSE + +################################################## +# Sources +################################################## +TOP_SRCS = \ +B100.v \ +u1plus_core.v \ +B100.ucf \ +timing.ucf + +SOURCES = $(abspath $(TOP_SRCS)) $(FIFO_SRCS) \ +$(CONTROL_LIB_SRCS) $(SDR_LIB_SRCS) $(SERDES_SRCS) \ +$(SIMPLE_GEMAC_SRCS) $(TIMING_SRCS) $(OPENCORES_SRCS) \ +$(VRT_SRCS) $(UDP_SRCS) $(COREGEN_SRCS) $(EXTRAM_SRCS) \ +$(GPIF_SRCS) + +################################################## +# Process Properties +################################################## +SYNTHESIZE_PROPERTIES = \ +"Number of Clock Buffers" 8 \ +"Pack I/O Registers into IOBs" Yes \ +"Optimization Effort" High \ +"Optimize Instantiated Primitives" TRUE \ +"Register Balancing" Yes \ +"Use Clock Enable" Auto \ +"Use Synchronous Reset" Auto \ +"Use Synchronous Set" Auto \ +"Verilog Macros" "NUM_RX_DSP=2 DISABLE_TX_DSP=1 $(CUSTOM_DEFS)" + +TRANSLATE_PROPERTIES = \ +"Macro Search Path" "$(shell pwd)/../../coregen/" + +MAP_PROPERTIES = \ +"Generate Detailed MAP Report" TRUE \ +"Allow Logic Optimization Across Hierarchy" TRUE \ +"Map to Input Functions" 4 \ +"Optimization Strategy (Cover Mode)" Speed \ +"Pack I/O Registers/Latches into IOBs" "For Inputs and Outputs" \ +"Perform Timing-Driven Packing and Placement" TRUE \ +"Map Effort Level" High \ +"Extra Effort" Normal \ +"Combinatorial Logic Optimization" TRUE \ +"Register Duplication" TRUE + +PLACE_ROUTE_PROPERTIES = \ +"Place & Route Effort Level (Overall)" High + +STATIC_TIMING_PROPERTIES = \ +"Number of Paths in Error/Verbose Report" 10 \ +"Report Type" "Error Report" + +GEN_PROG_FILE_PROPERTIES = \ +"Configuration Rate" 6 \ +"Create Binary Configuration File" TRUE \ +"Done (Output Events)" 5 \ +"Enable Bitstream Compression" TRUE \ +"Enable Outputs (Output Events)" 6 \ +"Unused IOB Pins" "Pull Up" + +SIM_MODEL_PROPERTIES = "" diff --git a/fpga/usrp2/top/B100/core_compile b/fpga/usrp2/top/B100/core_compile index b62cbaee0..2192bfa94 100755 --- a/fpga/usrp2/top/B100/core_compile +++ b/fpga/usrp2/top/B100/core_compile @@ -1 +1 @@ -iverilog -Wall -y. -y ../../control_lib/ -y ../../custom/ -y ../../fifo/ -y ../../gpif/ -y ../../models/ -y ../../sdr_lib/ -y ../../coregen/ -y ../../vrt/ -y ../../opencores/i2c/rtl/verilog/ -y ../../opencores/spi/rtl/verilog/ -y ../../timing/ -y ../../opencores/8b10b/ -I ../../opencores/spi/rtl/verilog/ -I ../../opencores/i2c/rtl/verilog/ -y ../../simple_gemac u1plus_core.v 2>&1 | grep -v timescale | grep -v coregen | grep -v models +iverilog -Wall -y. -y ../../control_lib/ -y ../../custom/ -y ../../fifo/ -y ../../gpif/ -y ../../models/ -y ../../sdr_lib/ -y ../../coregen/ -y ../../vrt/ -y ../../opencores/i2c/rtl/verilog/ -y ../../opencores/spi/rtl/verilog/ -y ../../timing/ -y ../../opencores/8b10b/ -I ../../opencores/spi/rtl/verilog/ -I ../../opencores/i2c/rtl/verilog/ -y ../../simple_gemac B100.v 2>&1 | grep -v timescale | grep -v coregen | grep -v models diff --git a/fpga/usrp2/top/B100/timing.ucf b/fpga/usrp2/top/B100/timing.ucf index 96c47cf2c..c4404e1d0 100644 --- a/fpga/usrp2/top/B100/timing.ucf +++ b/fpga/usrp2/top/B100/timing.ucf @@ -5,10 +5,15 @@ NET "IFCLK" TNM_NET = "IFCLK"; TIMESPEC "TS_IFCLK" = PERIOD "IFCLK" 20833 ps HIGH 50 %; #constrain FX2 IO -NET "GPIF_D<*>" MAXDELAY = 5.5 ns; -NET "GPIF_CTL<*>" MAXDELAY = 5.5 ns; -NET "GPIF_ADR<*>" MAXDELAY = 5.5ns; -NET "GPIF_SLWR" MAXDELAY = 5.5 ns; -NET "GPIF_SLRD" MAXDELAY = 5.5 ns; -NET "GPIF_SLOE" MAXDELAY = 5.5 ns; -NET "GPIF_PKTEND" MAXDELAY = 5.5 ns; +INST "GPIF_D<*>" TNM = gpif_net_in; +INST "GPIF_CTL<*>" TNM = gpif_net_in; + +INST "GPIF_D<*>" TNM = gpif_net_out; +INST "GPIF_ADR<*>" TNM = gpif_net_out; +INST "GPIF_SLWR" TNM = gpif_net_out; +INST "GPIF_SLOE" TNM = gpif_net_out; +INST "GPIF_SLRD" TNM = gpif_net_out; +INST "GPIF_PKTEND" TNM = gpif_net_out; + +TIMEGRP "gpif_net_in" OFFSET = IN 7 ns VALID 14 ns BEFORE "IFCLK" RISING; +TIMEGRP "gpif_net_out" OFFSET = OUT 7 ns AFTER "IFCLK" RISING; diff --git a/fpga/usrp2/top/B100/u1plus.ucf b/fpga/usrp2/top/B100/u1plus.ucf deleted file mode 100644 index 3ecc4daf2..000000000 --- a/fpga/usrp2/top/B100/u1plus.ucf +++ /dev/null @@ -1,203 +0,0 @@ -## Main Clock -NET "CLK_FPGA_P" LOC = "R7" ; -NET "CLK_FPGA_N" LOC = "T7" ; - -## UART -NET "FPGA_TXD" LOC = "H16" ; -NET "FPGA_RXD" LOC = "H12" ; - -## I2C -NET "SDA_FPGA" LOC = "T13" ; -NET "SCL_FPGA" LOC = "R13" ; - -## CGEN -NET "cgen_st_ld" LOC = "M13" ; -NET "cgen_st_refmon" LOC = "J14" ; -NET "cgen_st_status" LOC = "P6" ; -NET "cgen_ref_sel" LOC = "T2" ; -NET "cgen_sync_b" LOC = "H15" ; - -## FPGA Config -#NET "fpga_cfg_din" LOC = "T14" ; -#NET "fpga_cfg_cclk" LOC = "R14" ; -#NET "fpga_cfg_init_b" LOC = "T12" ; - -## MISC -#NET "mystery_bus<2>" LOC = "T11" ; -#NET "mystery_bus<1>" LOC = "C4" ; -#NET "mystery_bus<0>" LOC = "E7" ; -NET "reset_n" LOC = "D5" ; -NET "PPS_IN" LOC = "M14" ; -NET "reset_codec" LOC = "B14" ; - -## GPIF -NET "GPIF_D<15>" LOC = "P7" ; -NET "GPIF_D<14>" LOC = "N8" ; -NET "GPIF_D<13>" LOC = "T5" ; -NET "GPIF_D<12>" LOC = "T6" ; -NET "GPIF_D<11>" LOC = "N6" ; -NET "GPIF_D<10>" LOC = "P5" ; -NET "GPIF_D<9>" LOC = "R3" ; -NET "GPIF_D<8>" LOC = "T3" ; -NET "GPIF_D<7>" LOC = "N12" ; -NET "GPIF_D<6>" LOC = "P13" ; -NET "GPIF_D<5>" LOC = "P11" ; -NET "GPIF_D<4>" LOC = "R9" ; -NET "GPIF_D<3>" LOC = "T9" ; -NET "GPIF_D<2>" LOC = "N9" ; -NET "GPIF_D<1>" LOC = "P9" ; -NET "GPIF_D<0>" LOC = "P8" ; - -NET "GPIF_CTL<3>" LOC = "N5" ; -NET "GPIF_CTL<2>" LOC = "M11" ; -NET "GPIF_CTL<1>" LOC = "M9" ; -NET "GPIF_CTL<0>" LOC = "M7" ; - -NET "GPIF_RDY<3>" LOC = "N11" ; -NET "GPIF_RDY<2>" LOC = "T10" ; -NET "GPIF_RDY<1>" LOC = "T4" ; -NET "GPIF_RDY<0>" LOC = "R5" ; - -NET "FX2_PA7_FLAGD" LOC = "P12" ; -NET "FX2_PA6_PKTEND" LOC = "R11" ; -NET "FX2_PA2_SLOE" LOC = "P10" ; - -NET "IFCLK" LOC = "T8" ; - -## LEDs -NET "debug_led<2>" LOC = "R2" ; -NET "debug_led<1>" LOC = "N4" ; -NET "debug_led<0>" LOC = "P4" ; - -## Debug bus -NET "debug_clk<0>" LOC = "K15" ; -NET "debug_clk<1>" LOC = "K14" ; -NET "debug<0>" LOC = "K16" ; -NET "debug<1>" LOC = "J16" ; -NET "debug<2>" LOC = "C16" ; -NET "debug<3>" LOC = "C15" ; -NET "debug<4>" LOC = "E13" ; -NET "debug<5>" LOC = "D14" ; -NET "debug<6>" LOC = "D16" ; -NET "debug<7>" LOC = "D15" ; -NET "debug<8>" LOC = "E14" ; -NET "debug<9>" LOC = "F13" ; -NET "debug<10>" LOC = "G13" ; -NET "debug<11>" LOC = "F14" ; -NET "debug<12>" LOC = "E16" ; -NET "debug<13>" LOC = "F15" ; -NET "debug<14>" LOC = "H13" ; -NET "debug<15>" LOC = "G14" ; -NET "debug<16>" LOC = "G16" ; -NET "debug<17>" LOC = "F16" ; -NET "debug<18>" LOC = "J12" ; -NET "debug<19>" LOC = "J13" ; -NET "debug<20>" LOC = "L14" ; -NET "debug<21>" LOC = "L16" ; -NET "debug<22>" LOC = "M15" ; -NET "debug<23>" LOC = "M16" ; -NET "debug<24>" LOC = "L13" ; -NET "debug<25>" LOC = "K13" ; -NET "debug<26>" LOC = "P16" ; -NET "debug<27>" LOC = "N16" ; -NET "debug<28>" LOC = "R15" ; -NET "debug<29>" LOC = "P15" ; -NET "debug<30>" LOC = "N13" ; -NET "debug<31>" LOC = "N14" ; - -## ADC -NET "adc<11>" LOC = "B15" ; -NET "adc<10>" LOC = "A8" ; -NET "adc<9>" LOC = "B8" ; -NET "adc<8>" LOC = "C8" ; -NET "adc<7>" LOC = "D8" ; -NET "adc<6>" LOC = "C9" ; -NET "adc<5>" LOC = "A9" ; -NET "adc<4>" LOC = "C10" ; -NET "adc<3>" LOC = "D9" ; -NET "adc<2>" LOC = "A3" ; -NET "adc<1>" LOC = "B3" ; -NET "adc<0>" LOC = "A4" ; -NET "RXSYNC" LOC = "D10" ; - -## DAC -NET "TXBLANK" LOC = "K1" ; -NET "TXSYNC" LOC = "J2" ; -NET "dac<0>" LOC = "J1" ; -NET "dac<1>" LOC = "H3" ; -NET "dac<2>" LOC = "J3" ; -NET "dac<3>" LOC = "G2" ; -NET "dac<4>" LOC = "H1" ; -NET "dac<5>" LOC = "N3" ; -NET "dac<6>" LOC = "M4" ; -NET "dac<7>" LOC = "R1" ; -NET "dac<8>" LOC = "P2" ; -NET "dac<9>" LOC = "P1" ; -NET "dac<10>" LOC = "M1" ; -NET "dac<11>" LOC = "N1" ; -NET "dac<12>" LOC = "M3" ; -NET "dac<13>" LOC = "L4" ; - -## TX DB -NET "io_tx<0>" LOC = "K4" ; -NET "io_tx<1>" LOC = "L3" ; -NET "io_tx<2>" LOC = "L2" ; -NET "io_tx<3>" LOC = "F1" ; -NET "io_tx<4>" LOC = "F3" ; -NET "io_tx<5>" LOC = "G3" ; -NET "io_tx<6>" LOC = "E3" ; -NET "io_tx<7>" LOC = "E2" ; -NET "io_tx<8>" LOC = "E4" ; -NET "io_tx<9>" LOC = "F4" ; -NET "io_tx<10>" LOC = "D1" ; -NET "io_tx<11>" LOC = "E1" ; -NET "io_tx<12>" LOC = "D4" ; -NET "io_tx<13>" LOC = "D3" ; -NET "io_tx<14>" LOC = "C2" ; -NET "io_tx<15>" LOC = "C1" ; - -## RX DB -NET "io_rx<0>" LOC = "D7" ; -NET "io_rx<1>" LOC = "C6" ; -NET "io_rx<2>" LOC = "A6" ; -NET "io_rx<3>" LOC = "B6" ; -NET "io_rx<4>" LOC = "E9" ; -NET "io_rx<5>" LOC = "A7" ; -NET "io_rx<6>" LOC = "C7" ; -NET "io_rx<7>" LOC = "B10" ; -NET "io_rx<8>" LOC = "A10" ; -NET "io_rx<9>" LOC = "C11" ; -NET "io_rx<10>" LOC = "A11" ; -NET "io_rx<11>" LOC = "D11" ; -NET "io_rx<12>" LOC = "B12" ; -NET "io_rx<13>" LOC = "A12" ; -NET "io_rx<14>" LOC = "A14" ; -NET "io_rx<15>" LOC = "A13" ; - -## SPI -#NET "SEN_AUX" LOC = "C12" ; -#NET "SCLK_AUX" LOC = "D12" ; -#NET "MISO_AUX" LOC = "J5" ; -NET "SCLK_CODEC" LOC = "K3" ; -NET "SEN_CODEC" LOC = "D13" ; -NET "MOSI_CODEC" LOC = "C13" ; -NET "MISO_CODEC" LOC = "G4" ; - -NET "MISO_RX_DB" LOC = "E6" ; -NET "SEN_RX_DB" LOC = "B4" ; -NET "MOSI_RX_DB" LOC = "A5" ; -NET "SCLK_RX_DB" LOC = "C5" ; - -NET "MISO_TX_DB" LOC = "J4" ; -NET "SEN_TX_DB" LOC = "N2" ; -NET "MOSI_TX_DB" LOC = "L1" ; -NET "SCLK_TX_DB" LOC = "G1" ; - -## Dedicated pins -#NET "TMS" LOC = "B2" ; -#NET "TDO" LOC = "B16" ; -#NET "TDI" LOC = "B1" ; -#NET "TCK" LOC = "A15" ; - -#NET "fpga_cfg_prog_b" LOC = "A2" ; -#NET "fpga_cfg_done" LOC = "T15" ; diff --git a/fpga/usrp2/top/B100/u1plus.v b/fpga/usrp2/top/B100/u1plus.v deleted file mode 100644 index 5e3200580..000000000 --- a/fpga/usrp2/top/B100/u1plus.v +++ /dev/null @@ -1,173 +0,0 @@ -// -// 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/>. -// - -`timescale 1ns / 1ps -////////////////////////////////////////////////////////////////////////////////// - -module u1plus - (input CLK_FPGA_P, input CLK_FPGA_N, // Diff - output [2:0] debug_led, output [31:0] debug, output [1:0] debug_clk, - output FPGA_TXD, input FPGA_RXD, - - // GPIF - inout [15:0] GPIF_D, input [3:0] GPIF_CTL, output [3:0] GPIF_RDY, - output FX2_PA7_FLAGD, output FX2_PA6_PKTEND, output FX2_PA2_SLOE, - input IFCLK, - - inout SDA_FPGA, inout SCL_FPGA, // I2C - - output SCLK_TX_DB, output SEN_TX_DB, output MOSI_TX_DB, input MISO_TX_DB, // DB TX SPI - output SCLK_RX_DB, output SEN_RX_DB, output MOSI_RX_DB, input MISO_RX_DB, // DB TX SPI - output SCLK_CODEC, output SEN_CODEC, output MOSI_CODEC, input MISO_CODEC, // AD9862 main SPI - - input cgen_st_status, input cgen_st_ld, input cgen_st_refmon, output cgen_sync_b, output cgen_ref_sel, - - inout [15:0] io_tx, inout [15:0] io_rx, - - output [13:0] dac, output TXSYNC, output TXBLANK, - input [11:0] adc, input RXSYNC, - - input PPS_IN, - input reset_n, output reset_codec - ); - - assign reset_codec = 1; // Believed to be active low - - // ///////////////////////////////////////////////////////////////////////// - // Clocking - wire clk_fpga, clk_fpga_in, reset; - - IBUFGDS #(.IOSTANDARD("LVDS_33"), .DIFF_TERM("TRUE")) - clk_fpga_pin (.O(clk_fpga_in),.I(CLK_FPGA_P),.IB(CLK_FPGA_N)); - - BUFG clk_fpga_BUFG (.I(clk_fpga_in), .O(clk_fpga)); - - reset_sync reset_sync(.clk(clk_fpga), .reset_in(~reset_n), .reset_out(reset)); - - // ///////////////////////////////////////////////////////////////////////// - // SPI - wire mosi, sclk, miso; - assign { SCLK_TX_DB, MOSI_TX_DB } = ~SEN_TX_DB ? {sclk,mosi} : 2'b0; - assign { SCLK_RX_DB, MOSI_RX_DB } = ~SEN_RX_DB ? {sclk,mosi} : 2'b0; - assign { SCLK_CODEC, MOSI_CODEC } = ~SEN_CODEC ? {sclk,mosi} : 2'b0; - assign miso = (~SEN_TX_DB & MISO_TX_DB) | (~SEN_RX_DB & MISO_RX_DB) | - (~SEN_CODEC & MISO_CODEC); - - // ///////////////////////////////////////////////////////////////////////// - // TX DAC -- handle the interleaved data bus to DAC, with clock doubling DLL - - assign TXBLANK = 0; - wire [13:0] tx_i, tx_q; - - genvar i; - generate - for(i=0;i<14;i=i+1) - begin : gen_dacout - ODDR2 #(.DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1" - .INIT(1'b0), // Sets initial state of the Q output to 1'b0 or 1'b1 - .SRTYPE("SYNC")) // Specifies "SYNC" or "ASYNC" set/reset - ODDR2_inst (.Q(dac[i]), // 1-bit DDR output data - .C0(clk_fpga), // 1-bit clock input - .C1(~clk_fpga), // 1-bit clock input - .CE(1'b1), // 1-bit clock enable input - .D0(tx_i[i]), // 1-bit data input (associated with C0) - .D1(tx_q[i]), // 1-bit data input (associated with C1) - .R(1'b0), // 1-bit reset input - .S(1'b0)); // 1-bit set input - end // block: gen_dacout - endgenerate - ODDR2 #(.DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1" - .INIT(1'b0), // Sets initial state of the Q output to 1'b0 or 1'b1 - .SRTYPE("SYNC")) // Specifies "SYNC" or "ASYNC" set/reset - ODDR2_txsnc (.Q(TXSYNC), // 1-bit DDR output data - .C0(clk_fpga), // 1-bit clock input - .C1(~clk_fpga), // 1-bit clock input - .CE(1'b1), // 1-bit clock enable input - .D0(1'b0), // 1-bit data input (associated with C0) - .D1(1'b1), // 1-bit data input (associated with C1) - .R(1'b0), // 1-bit reset input - .S(1'b0)); // 1-bit set input - - // ///////////////////////////////////////////////////////////////////////// - // RX ADC -- handles deinterleaving - - reg [11:0] rx_i, rx_q; - wire [11:0] rx_a, rx_b; - - genvar j; - generate - for(j=0;j<12;j=j+1) - begin : gen_adcin - IDDR2 #(.DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1" - .INIT_Q0(1'b0), // Sets initial state of the Q0 output to 1’b0 or 1’b1 - .INIT_Q1(1'b0), // Sets initial state of the Q1 output to 1’b0 or 1’b1 - .SRTYPE("SYNC")) // Specifies "SYNC" or "ASYNC" set/reset - IDDR2_inst (.Q0(rx_a[j]), // 1-bit output captured with C0 clock - .Q1(rx_b[j]), // 1-bit output captured with C1 clock - .C0(clk_fpga), // 1-bit clock input - .C1(~clk_fpga), // 1-bit clock input - .CE(1'b1), // 1-bit clock enable input - .D(adc[j]), // 1-bit DDR data input - .R(1'b0), // 1-bit reset input - .S(1'b0)); // 1-bit set input - end // block: gen_adcin - endgenerate - - IDDR2 #(.DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1" - .INIT_Q0(1'b0), // Sets initial state of the Q0 output to 1’b0 or 1’b1 - .INIT_Q1(1'b0), // Sets initial state of the Q1 output to 1’b0 or 1’b1 - .SRTYPE("SYNC")) // Specifies "SYNC" or "ASYNC" set/reset - IDDR2_sync (.Q0(rxsync_0), // 1-bit output captured with C0 clock - .Q1(rxsync_1), // 1-bit output captured with C1 clock - .C0(clk_fpga), // 1-bit clock input - .C1(~clk_fpga), // 1-bit clock input - .CE(1'b1), // 1-bit clock enable input - .D(RXSYNC), // 1-bit DDR data input - .R(1'b0), // 1-bit reset input - .S(1'b0)); // 1-bit set input - - always @(posedge clk_fpga) - if(rxsync_0) - begin - rx_i <= rx_b; - rx_q <= rx_a; - end - else - begin - rx_i <= rx_a; - rx_q <= rx_b; - end - - // ///////////////////////////////////////////////////////////////////////// - // Main U1E Core - u1plus_core u1p_c(.clk_fpga(clk_fpga), .rst_fpga(reset), - .debug_led(debug_led), .debug(debug), .debug_clk(debug_clk), - .debug_txd(FPGA_TXD), .debug_rxd(FPGA_RXD), - .gpif_d(GPIF_D), .gpif_ctl(GPIF_CTL), .gpif_rdy(GPIF_RDY), - .gpif_misc({FX2_PA7_FLAGD,FX2_PA6_PKTEND,FX2_PA2_SLOE}), - .gpif_clk(IFCLK), - - .db_sda(SDA_FPGA), .db_scl(SCL_FPGA), - .sclk(sclk), .sen({SEN_CODEC,SEN_TX_DB,SEN_RX_DB}), .mosi(mosi), .miso(miso), - .cgen_st_status(cgen_st_status), .cgen_st_ld(cgen_st_ld),.cgen_st_refmon(cgen_st_refmon), - .cgen_sync_b(cgen_sync_b), .cgen_ref_sel(cgen_ref_sel), - .io_tx(io_tx), .io_rx(io_rx), - .tx_i(tx_i), .tx_q(tx_q), - .rx_i(rx_i), .rx_q(rx_q), - .pps_in(PPS_IN) ); - -endmodule // u1plus diff --git a/fpga/usrp2/top/B100/u1plus_core.v b/fpga/usrp2/top/B100/u1plus_core.v index c1d6767d1..ef0ce51f7 100644 --- a/fpga/usrp2/top/B100/u1plus_core.v +++ b/fpga/usrp2/top/B100/u1plus_core.v @@ -18,435 +18,345 @@ module u1plus_core - (input clk_fpga, input rst_fpga, - output [2:0] debug_led, output [31:0] debug, output [1:0] debug_clk, - output debug_txd, input debug_rxd, - - // GPIF - inout [15:0] gpif_d, input [3:0] gpif_ctl, output gpif_sloe, - output gpif_slwr, output gpif_slrd, output gpif_pktend, output [1:0] gpif_fifoadr, - input gpif_clk, - +#( + parameter NUM_RX_DSPS = 2, + parameter CTRL_ACK_SID = 20, //needed for reply + + parameter DSP_TX_FIFOSIZE = 10, //4K MTU + parameter DSP_RX_FIFOSIZE = 10, //4K MTU + + parameter DSP_RX_XTRA_FIFOSIZE = 11, + parameter DSP_TX_XTRA_FIFOSIZE = 11, + + parameter USE_PACKET_PADDER = 0 +) + (input clk, input reset, + output [31:0] debug, output [1:0] debug_clk, + + // Host Interface + input [35:0] tx_data, input tx_src_rdy, output tx_dst_rdy, + output [35:0] rx_data, output rx_src_rdy, input rx_dst_rdy, + input [35:0] ctrl_data, input ctrl_src_rdy, output ctrl_dst_rdy, + output [35:0] resp_data, output resp_src_rdy, input resp_dst_rdy, + + output dsp_rx_run, output dsp_tx_run, output clock_sync, + inout db_sda, inout db_scl, - output sclk, output [15:0] sen, output mosi, input miso, + output sclk, output [7:0] sen, output mosi, input miso, - input cgen_st_status, input cgen_st_ld, input cgen_st_refmon, output cgen_sync_b, output cgen_ref_sel, - inout [15:0] io_tx, inout [15:0] io_rx, - output [13:0] tx_i, output [13:0] tx_q, - input [11:0] rx_i, input [11:0] rx_q, + inout [15:0] io_tx, inout [15:0] io_rx, + output [13:0] tx_i, output [13:0] tx_q, + input [11:0] rx_i, input [11:0] rx_q, input pps_in ); - localparam TXFIFOSIZE = 11; - localparam RXFIFOSIZE = 12; - - // 64 total regs in address space - localparam SR_RX_CTRL0 = 0; // 9 regs (+0 to +8) - localparam SR_RX_DSP0 = 10; // 4 regs (+0 to +3) - localparam SR_RX_CTRL1 = 16; // 9 regs (+0 to +8) - localparam SR_RX_DSP1 = 26; // 4 regs (+0 to +3) - localparam SR_TX_CTRL = 32; // 4 regs (+0 to +3) - localparam SR_TX_DSP = 38; // 3 regs (+0 to +2) - - localparam SR_TIME64 = 42; // 6 regs (+0 to +5) - localparam SR_RX_FRONT = 48; // 5 regs (+0 to +4) - localparam SR_TX_FRONT = 54; // 5 regs (+0 to +4) - - localparam SR_REG_TEST32 = 60; // 1 reg - localparam SR_CLEAR_FIFO = 61; // 1 reg - localparam SR_GLOBAL_RESET = 63; // 1 reg - localparam SR_USER_REGS = 64; // 2 regs - - localparam SR_GPIO = 128; // 5 regs - - wire wb_clk = clk_fpga; - wire wb_rst, global_reset; - - wire pps_int; - wire [63:0] vita_time, vita_time_pps; - reg [15:0] reg_cgen_ctrl, reg_test; - - wire [7:0] set_addr, set_addr_user; - wire [31:0] set_data, set_data_user; - wire set_stb, set_stb_user; - - wire [31:0] debug0; - wire [31:0] debug1; - - wire [31:0] debug_vt; - wire gpif_rst; - - reg [7:0] frames_per_packet; - - wire rx_overrun_dsp0, rx_overrun_dsp1, rx_overrun_gpif, tx_underrun_dsp, tx_underrun_gpif; - wire rx_overrun = rx_overrun_gpif | rx_overrun_dsp0 | rx_overrun_dsp1; - wire tx_underrun = tx_underrun_gpif | tx_underrun_dsp; - - 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_wb(.clk(wb_clk), .reset_in(rst_fpga | global_reset), .reset_out(wb_rst)); - reset_sync reset_sync_gp(.clk(gpif_clk), .reset_in(rst_fpga | global_reset), .reset_out(gpif_rst)); - - // ///////////////////////////////////////////////////////////////////////////////////// - // GPIF Slave to Wishbone Master - localparam dw = 16; - localparam aw = 11; - localparam sw = 2; - - wire [dw-1:0] m0_dat_mosi, m0_dat_miso; - wire [aw-1:0] m0_adr; - wire [sw-1:0] m0_sel; - wire m0_cyc, m0_stb, m0_we, m0_ack, m0_err, m0_rty; - - wire [31:0] debug_gpif; - - wire [35:0] tx_data, rx_data, tx_err_data; - wire tx_src_rdy, tx_dst_rdy, rx_src_rdy, rx_dst_rdy, - tx_err_src_rdy, tx_err_dst_rdy; - - wire clear_fifo; - - setting_reg #(.my_addr(SR_CLEAR_FIFO), .width(1)) sr_clear_fifo - (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(),.changed(clear_fifo)); - - wire run_rx0, run_rx1; - - slave_fifo #(.TXFIFOSIZE(TXFIFOSIZE), .RXFIFOSIZE(RXFIFOSIZE)) - slave_fifo (.gpif_clk(gpif_clk), .gpif_rst(gpif_rst), .gpif_d(gpif_d), - .gpif_ctl(gpif_ctl), .sloe(gpif_sloe), .slwr(gpif_slwr), .slrd(gpif_slrd), - .pktend(gpif_pktend), .fifoadr(gpif_fifoadr), - - .wb_clk(wb_clk), .wb_rst(wb_rst), - .wb_adr_o(m0_adr), .wb_dat_mosi(m0_dat_mosi), .wb_dat_miso(m0_dat_miso), - .wb_sel_o(m0_sel), .wb_cyc_o(m0_cyc), .wb_stb_o(m0_stb), .wb_we_o(m0_we), - .wb_ack_i(m0_ack), .triggers(8'd0), - - .dsp_rx_run(run_rx0 | run_rx1), - - .fifo_clk(wb_clk), .fifo_rst(wb_rst), .clear_tx(clear_fifo), .clear_rx(clear_fifo), - .tx_data_o(tx_data), .tx_src_rdy_o(tx_src_rdy), .tx_dst_rdy_i(tx_dst_rdy), - .rx_data_i(rx_data), .rx_src_rdy_i(rx_src_rdy), .rx_dst_rdy_o(rx_dst_rdy), - .tx_err_data_i(tx_err_data), .tx_err_src_rdy_i(tx_err_src_rdy), .tx_err_dst_rdy_o(tx_err_dst_rdy), - - .tx_underrun(tx_underrun_gpif), .rx_overrun(rx_overrun_gpif), - - .test_len(0), .test_rate(0), .test_ctrl(0), - .debug0(debug0), .debug1(debug1)); + localparam SR_MISC = 0; // 5 + localparam SR_USER_REGS = 5; // 2 + localparam SR_PADDER = 10; // 2 + + localparam SR_TX_CTRL = 32; // 6 + localparam SR_TX_DSP = 40; // 5 + localparam SR_TX_FE = 48; // 5 + + localparam SR_RX_CTRL0 = 96; // 9 + localparam SR_RX_DSP0 = 106; // 7 + localparam SR_RX_FE = 114; // 5 + + localparam SR_RX_CTRL1 = 128; // 9 + localparam SR_RX_DSP1 = 138; // 7 + + localparam SR_TIME64 = 192; // 6 + localparam SR_SPI = 208; // 3 + localparam SR_I2C = 216; // 1 + localparam SR_GPIO = 224; // 5 + + //compatibility number -> increment when the fpga has been sufficiently altered + localparam compat_num = {16'd11, 16'd0}; //major, minor + + //assign run signals used for ATR logic + wire [NUM_RX_DSPS-1:0] run_rx_n; + wire run_tx; + wire run_rx = |(run_rx_n); + assign dsp_rx_run = run_rx; + assign dsp_tx_run = run_tx; + + //shared time core signals + wire [63:0] vita_time, vita_time_pps; + + //shared settings bus signals + wire set_stb, set_stb_user; + wire [31:0] set_data, set_data_user; + wire [7:0] set_addr, set_addr_user; + + //shared SPI core signals + wire [31:0] spi_readback; + wire spi_ready; + + //shared I2C core signals + wire [31:0] i2c_readback; + wire i2c_ready; + + //shared GPIO core signals + wire [31:0] gpio_readback; + + /////////////////////////////////////////////////////////////////////////// + // Misc Registers - persistent across resets + /////////////////////////////////////////////////////////////////////////// + wire [31:0] config_word0; + setting_reg #(.my_addr(SR_MISC+0), .width(32)) sr_misc_config0 + (.clk(clk), .rst(1'b0/*reset*/), .strobe(set_stb), .addr(set_addr), .in(set_data), .out(config_word0)); + + wire [31:0] config_word1; + setting_reg #(.my_addr(SR_MISC+1), .width(32)) sr_misc_config1 + (.clk(clk), .rst(1'b0/*reset*/), .strobe(set_stb), .addr(set_addr), .in(set_data), .out(config_word1)); + + wire clock_sync_inv, clock_sync_enb; + setting_reg #(.my_addr(SR_MISC+2), .width(2)) sr_misc_clock_sync + (.clk(clk), .rst(reset), .strobe(set_stb), .addr(set_addr), .in(set_data), + .out({clock_sync_inv, clock_sync_enb})); + + /////////////////////////////////////////////////////////////////////////// + // Settings Bus and Readback + /////////////////////////////////////////////////////////////////////////// + user_settings #(.BASE(SR_USER_REGS)) user_settings + (.clk(clk),.rst(reset), + .set_stb(set_stb), .set_addr(set_addr),.set_data(set_data), + .set_addr_user(set_addr_user),.set_data_user(set_data_user), .set_stb_user(set_stb_user) ); + + wire [35:0] ctrl_out_data, ctrl_int_data; + wire ctrl_out_src_rdy, ctrl_out_dst_rdy; + wire ctrl_int_src_rdy, ctrl_int_dst_rdy; + + fifo_cascade #(.WIDTH(36), .SIZE(9)) ctrl_fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(ctrl_data), .src_rdy_i(ctrl_src_rdy), .dst_rdy_o(ctrl_dst_rdy), .space(), + .dataout(ctrl_int_data), .src_rdy_o(ctrl_int_src_rdy), .dst_rdy_i(ctrl_int_dst_rdy), .occupied()); + + wire [31:0] num_rx_dsps_rb = NUM_RX_DSPS; + + wire [31:0] sfc_debug; + settings_fifo_ctrl #(.PROT_HDR(0), .ACK_SID(CTRL_ACK_SID), .XPORT_HDR(0)) sfc + ( + .clock(clk), .reset(reset), .clear(1'b0), + .vita_time(vita_time), .perfs_ready(spi_ready & i2c_ready), + .in_data(ctrl_int_data), .in_valid(ctrl_int_src_rdy), .in_ready(ctrl_int_dst_rdy), + .out_data(ctrl_out_data), .out_valid(ctrl_out_src_rdy), .out_ready(ctrl_out_dst_rdy), + .strobe(set_stb), .addr(set_addr), .data(set_data), + .word00(spi_readback),.word01(compat_num),.word02(i2c_readback),.word03(gpio_readback), + .word04(config_word0),.word05(config_word1),.word06(num_rx_dsps_rb),.word07(32'hffff_ffff), + .word08(32'hffff_ffff),.word09(32'hffff_ffff),.word10(vita_time[63:32]), + .word11(vita_time[31:0]),.word12(32'hffff_ffff),.word13(32'hffff_ffff), + .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]), + .debug(sfc_debug) + ); + + /////////////////////////////////////////////////////////////////////////// + // Time Core + /////////////////////////////////////////////////////////////////////////// + time_64bit #(.BASE(SR_TIME64)) time_64bit + (.clk(clk), .rst(reset), .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .pps(pps_in), .vita_time(vita_time), .vita_time_pps(vita_time_pps), + .exp_time_in(0)); + + assign clock_sync = (clock_sync_enb)? (pps_in ^ clock_sync_inv) : 1'b0; + + /////////////////////////////////////////////////////////////////////////// + // SPI Core + /////////////////////////////////////////////////////////////////////////// + simple_spi_core #(.BASE(SR_SPI), .WIDTH(8), .CLK_IDLE(0), .SEN_IDLE(8'hff)) + simple_spi_core (.clock(clk), .reset(reset), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .readback(spi_readback), .ready(spi_ready), + .sen(sen), .sclk(sclk), .mosi(mosi), .miso(miso)); + + /////////////////////////////////////////////////////////////////////////// + // I2C Core + /////////////////////////////////////////////////////////////////////////// + wire scl_pad_i, scl_pad_o, scl_pad_oen_o, sda_pad_i, sda_pad_o, sda_pad_oen_o; + simple_i2c_core #(.BASE(SR_I2C)) i2c_core + (.clock(clk),.reset(reset), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .readback(i2c_readback), .ready(i2c_ready), + .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_pad_oen_o), + .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_pad_oen_o) ); + + // I2C -- Don't use external transistors for open drain, the FPGA implements this + IOBUF scl_pin(.O(scl_pad_i), .IO(db_scl), .I(scl_pad_o), .T(scl_pad_oen_o)); + IOBUF sda_pin(.O(sda_pad_i), .IO(db_sda), .I(sda_pad_o), .T(sda_pad_oen_o)); + + /////////////////////////////////////////////////////////////////////////// + // GPIO Core + /////////////////////////////////////////////////////////////////////////// + gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) + gpio_atr(.clk(clk),.reset(reset), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .rx(run_rx), .tx(run_tx), .gpio({io_tx, io_rx}), .gpio_readback(gpio_readback) ); // ///////////////////////////////////////////////////////////////////////// // RX ADC Frontend, does IQ Balance, DC Offset, muxing wire [23:0] rx_fe_i, rx_fe_q; // 24 bits is total overkill here, but it matches u2/u2p - rx_frontend #(.BASE(SR_RX_FRONT)) rx_frontend - (.clk(wb_clk),.rst(wb_rst), + rx_frontend #(.BASE(SR_RX_FE)) rx_frontend + (.clk(clk),.rst(reset), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .adc_a({rx_i,4'b00}),.adc_ovf_a(0), .adc_b({rx_q,4'b00}),.adc_ovf_b(0), - .i_out(rx_fe_i), .q_out(rx_fe_q), .run(run_rx0 | run_rx1), .debug()); - - // ///////////////////////////////////////////////////////////////////////// - // DSP RX 0 - - wire [31:0] sample_rx0; - wire strobe_rx0, clear_rx0; - wire [35:0] vita_rx_data0; - wire vita_rx_src_rdy0, vita_rx_dst_rdy0; - - ddc_chain #(.BASE(SR_RX_DSP0), .DSPNO(0)) ddc_chain0 - (.clk(wb_clk), .rst(wb_rst), .clr(clear_rx0), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), - .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), - .debug() ); + .i_out(rx_fe_i), .q_out(rx_fe_q), .run(run_rx), .debug()); - vita_rx_chain #(.BASE(SR_RX_CTRL0), .UNIT(0), .FIFOSIZE(10), .PROT_ENG_FLAGS(0), .DSP_NUMBER(0)) vita_rx_chain0 - (.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .vita_time(vita_time), .overrun(rx_overrun_dsp0), - .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), .clear_o(clear_rx0), - .rx_data_o(vita_rx_data0), .rx_dst_rdy_i(vita_rx_dst_rdy0), .rx_src_rdy_o(vita_rx_src_rdy0), - .debug() ); - // ///////////////////////////////////////////////////////////////////////// - // DSP RX 1 - - wire [31:0] sample_rx1; - wire strobe_rx1, clear_rx1; - wire [35:0] vita_rx_data1; - wire vita_rx_src_rdy1, vita_rx_dst_rdy1; - - ddc_chain #(.BASE(SR_RX_DSP1), .DSPNO(1)) ddc_chain1 - (.clk(wb_clk),.rst(wb_rst), .clr(clear_rx1), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), - .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), - .debug() ); - - vita_rx_chain #(.BASE(SR_RX_CTRL1), .UNIT(1), .FIFOSIZE(10), .PROT_ENG_FLAGS(0), .DSP_NUMBER(1)) vita_rx_chain1 - (.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .vita_time(vita_time), .overrun(rx_overrun_dsp1), - .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), .clear_o(clear_rx1), - .rx_data_o(vita_rx_data1), .rx_dst_rdy_i(vita_rx_dst_rdy1), .rx_src_rdy_o(vita_rx_src_rdy1), - .debug() ); + // DSP RX * + + wire [35:0] rx_int2_data [NUM_RX_DSPS-1:0]; + wire rx_int2_src_rdy [NUM_RX_DSPS-1:0]; + wire rx_int2_dst_rdy [NUM_RX_DSPS-1:0]; + + genvar dspno; + generate + for(dspno = 0; dspno < NUM_RX_DSPS; dspno = dspno + 1) begin:gen_rx_dsps + + wire [31:0] sample_rx; + wire strobe_rx, clear_rx; + wire [35:0] vita_rx_data; + wire vita_rx_src_rdy, vita_rx_dst_rdy; + wire [35:0] int_rx_data; + wire int_rx_src_rdy, int_rx_dst_rdy; + + ddc_chain #(.BASE(SR_RX_DSP0+dspno*32), .DSPNO(dspno)) ddc_chain + (.clk(clk), .rst(reset), .clr(clear_rx), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), + .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), + .sample(sample_rx), .run(run_rx_n[dspno]), .strobe(strobe_rx), + .debug() ); + + vita_rx_chain #(.BASE(SR_RX_CTRL0+dspno*32), .UNIT(dspno), .FIFOSIZE(DSP_RX_FIFOSIZE), .PROT_ENG_FLAGS(0), .DSP_NUMBER(dspno)) vita_rx_chain + (.clk(clk),.reset(reset), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), + .vita_time(vita_time), .overrun(), + .sample(sample_rx), .run(run_rx_n[dspno]), .strobe(strobe_rx), .clear_o(clear_rx), + .rx_data_o(vita_rx_data), .rx_dst_rdy_i(vita_rx_dst_rdy), .rx_src_rdy_o(vita_rx_src_rdy), + .debug() ); + + fifo_cascade #(.WIDTH(36), .SIZE(DSP_RX_FIFOSIZE+1)) rx_data_fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(vita_rx_data), .src_rdy_i(vita_rx_src_rdy), .dst_rdy_o(vita_rx_dst_rdy), .space(), + .dataout(int_rx_data), .src_rdy_o(int_rx_src_rdy), .dst_rdy_i(int_rx_dst_rdy), .occupied()); + + if (dspno == 0) begin + assign rx_int2_data[dspno] = int_rx_data; + assign rx_int2_src_rdy[dspno] = int_rx_src_rdy; + assign int_rx_dst_rdy = rx_int2_dst_rdy[dspno]; + end + else begin + fifo36_mux #(.prio(0)) // No priority, fair sharing + combine_rx_dsps ( + .clk(clk), .reset(reset), .clear(1'b0/*noclear*/), + .data0_i(rx_int2_data[dspno-1]), .src0_rdy_i(rx_int2_src_rdy[dspno-1]), .dst0_rdy_o(rx_int2_dst_rdy[dspno-1]), + .data1_i(int_rx_data), .src1_rdy_i(int_rx_src_rdy), .dst1_rdy_o(int_rx_dst_rdy), + .data_o(rx_int2_data[dspno]), .src_rdy_o(rx_int2_src_rdy[dspno]), .dst_rdy_i(rx_int2_dst_rdy[dspno]) + ); + end + + end + endgenerate // ///////////////////////////////////////////////////////////////////////// // RX Stream muxing - fifo36_mux #(.prio(0)) mux_data_streams - (.clk(wb_clk), .reset(wb_rst), .clear(clear_fifo), - .data0_i(vita_rx_data0), .src0_rdy_i(vita_rx_src_rdy0), .dst0_rdy_o(vita_rx_dst_rdy0), - .data1_i(vita_rx_data1), .src1_rdy_i(vita_rx_src_rdy1), .dst1_rdy_o(vita_rx_dst_rdy1), - .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); + wire [35:0] rx_int3_data; + wire rx_int3_src_rdy, rx_int3_dst_rdy; + + fifo_cascade #(.WIDTH(36), .SIZE(DSP_RX_XTRA_FIFOSIZE)) rx_data_fifo_combined + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(rx_int2_data[NUM_RX_DSPS-1]), .src_rdy_i(rx_int2_src_rdy[NUM_RX_DSPS-1]), .dst_rdy_o(rx_int2_dst_rdy[NUM_RX_DSPS-1]), .space(), + .dataout(rx_int3_data), .src_rdy_o(rx_int3_src_rdy), .dst_rdy_i(rx_int3_dst_rdy), .occupied()); + + generate + if (USE_PACKET_PADDER) begin + packet_padder36 #(.BASE(SR_PADDER)) packet_padder_rx_data36( + .clk(clk), .reset(reset), + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + .data_i(rx_int3_data), .src_rdy_i(rx_int3_src_rdy), .dst_rdy_o(rx_int3_dst_rdy), + .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy), + .always_flush(~dsp_rx_run)); + end + else begin + assign rx_data = rx_int3_data; + assign rx_src_rdy = rx_int3_src_rdy; + assign rx_int3_dst_rdy = rx_dst_rdy; + end + endgenerate + + /////////////////////////////////////////////////////////////////////////// + // MUX for TX async and resp data + /////////////////////////////////////////////////////////////////////////// + wire [35:0] tx_err_data, resp_data_int; + wire tx_err_src_rdy, resp_src_rdy_int; + wire tx_err_dst_rdy, resp_dst_rdy_int; + + fifo36_mux #(.prio(0)) // No priority, fair sharing + combine_async_and_resp ( + .clk(clk), .reset(reset), .clear(1'b0/*noclear*/), + .data0_i(ctrl_out_data), .src0_rdy_i(ctrl_out_src_rdy), .dst0_rdy_o(ctrl_out_dst_rdy), + .data1_i(tx_err_data), .src1_rdy_i(tx_err_src_rdy), .dst1_rdy_o(tx_err_dst_rdy), + .data_o(resp_data_int), .src_rdy_o(resp_src_rdy_int), .dst_rdy_i(resp_dst_rdy_int) + ); + + fifo_cascade #(.WIDTH(36), .SIZE(9)) resp_fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .datain(resp_data_int), .src_rdy_i(resp_src_rdy_int), .dst_rdy_o(resp_dst_rdy_int), .space(), + .dataout(resp_data), .src_rdy_o(resp_src_rdy), .dst_rdy_i(resp_dst_rdy), .occupied()); // /////////////////////////////////////////////////////////////////////////////////// // DSP TX - wire run_tx; wire [23:0] tx_fe_i, tx_fe_q; wire [31:0] sample_tx; wire strobe_tx, clear_tx; - vita_tx_chain #(.BASE(SR_TX_CTRL), .FIFOSIZE(10), .POST_ENGINE_FIFOSIZE(11), +`ifdef DISABLE_TX_DSP + assign tx_dst_rdy = 1; //null sink + assign run_tx = 0; + assign tx_i = 0; + assign tx_q = 0; +`else + vita_tx_chain #(.BASE(SR_TX_CTRL), + .FIFOSIZE(DSP_TX_FIFOSIZE), + .POST_ENGINE_FIFOSIZE(DSP_TX_XTRA_FIFOSIZE), .REPORT_ERROR(1), .DO_FLOW_CONTROL(0), .PROT_ENG_FLAGS(0), .USE_TRANS_HEADER(0), - .DSP_NUMBER(0)) + .DSP_NUMBER(0)) vita_tx_chain - (.clk(wb_clk), .reset(wb_rst), + (.clk(clk), .reset(reset), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), .vita_time(vita_time), .tx_data_i(tx_data), .tx_src_rdy_i(tx_src_rdy), .tx_dst_rdy_o(tx_dst_rdy), .err_data_o(tx_err_data), .err_src_rdy_o(tx_err_src_rdy), .err_dst_rdy_i(tx_err_dst_rdy), .sample(sample_tx), .strobe(strobe_tx), - .underrun(tx_underrun_dsp), .run(run_tx), .clear_o(clear_tx), - .debug(debug_vt)); + .underrun(), .run(run_tx), .clear_o(clear_tx), + .debug()); duc_chain #(.BASE(SR_TX_DSP), .DSPNO(0)) duc_chain - (.clk(wb_clk), .rst(wb_rst), .clr(clear_tx), + (.clk(clk), .rst(reset), .clr(clear_tx), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), .tx_fe_i(tx_fe_i),.tx_fe_q(tx_fe_q), .sample(sample_tx), .run(run_tx), .strobe(strobe_tx), .debug() ); - tx_frontend #(.BASE(SR_TX_FRONT), .WIDTH_OUT(14)) tx_frontend - (.clk(wb_clk), .rst(wb_rst), + tx_frontend #(.BASE(SR_TX_FE), .WIDTH_OUT(14)) tx_frontend + (.clk(clk), .rst(reset), .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), .tx_i(tx_fe_i), .tx_q(tx_fe_q), .run(1'b1), .dac_a(tx_i), .dac_b(tx_q)); - - // ///////////////////////////////////////////////////////////////////////////////////// - // Wishbone Intercon, single master - wire [dw-1:0] s0_dat_mosi, s1_dat_mosi, s0_dat_miso, s1_dat_miso, s2_dat_mosi, s3_dat_mosi, s2_dat_miso, s3_dat_miso, - s4_dat_mosi, s5_dat_mosi, s4_dat_miso, s5_dat_miso, s6_dat_mosi, s7_dat_mosi, s6_dat_miso, s7_dat_miso, - s8_dat_mosi, s9_dat_mosi, s8_dat_miso, s9_dat_miso, sa_dat_mosi, sb_dat_mosi, sa_dat_miso, sb_dat_miso, - sc_dat_mosi, sd_dat_mosi, sc_dat_miso, sd_dat_miso, se_dat_mosi, sf_dat_mosi, se_dat_miso, sf_dat_miso; - wire [aw-1:0] s0_adr,s1_adr,s2_adr,s3_adr,s4_adr,s5_adr,s6_adr,s7_adr; - wire [aw-1:0] s8_adr,s9_adr,sa_adr,sb_adr,sc_adr, sd_adr, se_adr, sf_adr; - wire [sw-1:0] s0_sel,s1_sel,s2_sel,s3_sel,s4_sel,s5_sel,s6_sel,s7_sel; - wire [sw-1:0] s8_sel,s9_sel,sa_sel,sb_sel,sc_sel, sd_sel, se_sel, sf_sel; - wire s0_ack,s1_ack,s2_ack,s3_ack,s4_ack,s5_ack,s6_ack,s7_ack; - wire s8_ack,s9_ack,sa_ack,sb_ack,sc_ack, sd_ack, se_ack, sf_ack; - wire s0_stb,s1_stb,s2_stb,s3_stb,s4_stb,s5_stb,s6_stb,s7_stb; - wire s8_stb,s9_stb,sa_stb,sb_stb,sc_stb, sd_stb, se_stb, sf_stb; - wire s0_cyc,s1_cyc,s2_cyc,s3_cyc,s4_cyc,s5_cyc,s6_cyc,s7_cyc; - wire s8_cyc,s9_cyc,sa_cyc,sb_cyc,sc_cyc, sd_cyc, se_cyc, sf_cyc; - wire s0_we,s1_we,s2_we,s3_we,s4_we,s5_we,s6_we,s7_we; - wire s8_we,s9_we,sa_we,sb_we,sc_we,sd_we, se_we, sf_we; - - wb_1master #(.dw(dw), .aw(aw), .sw(sw), .decode_w(4), - .s0_addr(4'h0), .s0_mask(4'hF), // Misc Regs - .s1_addr(4'h1), .s1_mask(4'hF), // Unused - .s2_addr(4'h2), .s2_mask(4'hF), // SPI - .s3_addr(4'h3), .s3_mask(4'hF), // I2C - .s4_addr(4'h4), .s4_mask(4'hF), // Unused - .s5_addr(4'h5), .s5_mask(4'hF), // Unused on B1x0, Async Msg on E1x0 - .s6_addr(4'h6), .s6_mask(4'hF), // Unused - .s7_addr(4'h7), .s7_mask(4'hF), // Readback MUX - .s8_addr(4'h8), .s8_mask(4'h8), // Setting Regs -- slave 8 is 8 slaves wide - // slaves 9-f alias to slave 1, all are unused - .s9_addr(4'h1), .s9_mask(4'hF), - .sa_addr(4'h1), .sa_mask(4'hF), .sb_addr(4'h1), .sb_mask(4'hF), - .sc_addr(4'h1), .sc_mask(4'hF), .sd_addr(4'h1), .sd_mask(4'hF), - .se_addr(4'h1), .se_mask(4'hF), .sf_addr(4'h1), .sf_mask(4'hF)) - wb_1master - (.clk_i(wb_clk),.rst_i(wb_rst), - .m0_dat_o(m0_dat_miso),.m0_ack_o(m0_ack),.m0_err_o(m0_err),.m0_rty_o(m0_rty),.m0_dat_i(m0_dat_mosi), - .m0_adr_i(m0_adr),.m0_sel_i(m0_sel),.m0_we_i(m0_we),.m0_cyc_i(m0_cyc),.m0_stb_i(m0_stb), - .s0_dat_o(s0_dat_mosi),.s0_adr_o(s0_adr),.s0_sel_o(s0_sel),.s0_we_o(s0_we),.s0_cyc_o(s0_cyc),.s0_stb_o(s0_stb), - .s0_dat_i(s0_dat_miso),.s0_ack_i(s0_ack),.s0_err_i(0),.s0_rty_i(0), - .s1_dat_o(s1_dat_mosi),.s1_adr_o(s1_adr),.s1_sel_o(s1_sel),.s1_we_o(s1_we),.s1_cyc_o(s1_cyc),.s1_stb_o(s1_stb), - .s1_dat_i(s1_dat_miso),.s1_ack_i(s1_ack),.s1_err_i(0),.s1_rty_i(0), - .s2_dat_o(s2_dat_mosi),.s2_adr_o(s2_adr),.s2_sel_o(s2_sel),.s2_we_o(s2_we),.s2_cyc_o(s2_cyc),.s2_stb_o(s2_stb), - .s2_dat_i(s2_dat_miso),.s2_ack_i(s2_ack),.s2_err_i(0),.s2_rty_i(0), - .s3_dat_o(s3_dat_mosi),.s3_adr_o(s3_adr),.s3_sel_o(s3_sel),.s3_we_o(s3_we),.s3_cyc_o(s3_cyc),.s3_stb_o(s3_stb), - .s3_dat_i(s3_dat_miso),.s3_ack_i(s3_ack),.s3_err_i(0),.s3_rty_i(0), - .s4_dat_o(s4_dat_mosi),.s4_adr_o(s4_adr),.s4_sel_o(s4_sel),.s4_we_o(s4_we),.s4_cyc_o(s4_cyc),.s4_stb_o(s4_stb), - .s4_dat_i(s4_dat_miso),.s4_ack_i(s4_ack),.s4_err_i(0),.s4_rty_i(0), - .s5_dat_o(s5_dat_mosi),.s5_adr_o(s5_adr),.s5_sel_o(s5_sel),.s5_we_o(s5_we),.s5_cyc_o(s5_cyc),.s5_stb_o(s5_stb), - .s5_dat_i(s5_dat_miso),.s5_ack_i(s5_ack),.s5_err_i(0),.s5_rty_i(0), - .s6_dat_o(s6_dat_mosi),.s6_adr_o(s6_adr),.s6_sel_o(s6_sel),.s6_we_o(s6_we),.s6_cyc_o(s6_cyc),.s6_stb_o(s6_stb), - .s6_dat_i(s6_dat_miso),.s6_ack_i(s6_ack),.s6_err_i(0),.s6_rty_i(0), - .s7_dat_o(s7_dat_mosi),.s7_adr_o(s7_adr),.s7_sel_o(s7_sel),.s7_we_o(s7_we),.s7_cyc_o(s7_cyc),.s7_stb_o(s7_stb), - .s7_dat_i(s7_dat_miso),.s7_ack_i(s7_ack),.s7_err_i(0),.s7_rty_i(0), - .s8_dat_o(s8_dat_mosi),.s8_adr_o(s8_adr),.s8_sel_o(s8_sel),.s8_we_o(s8_we),.s8_cyc_o(s8_cyc),.s8_stb_o(s8_stb), - .s8_dat_i(s8_dat_miso),.s8_ack_i(s8_ack),.s8_err_i(0),.s8_rty_i(0), - .s9_dat_o(s9_dat_mosi),.s9_adr_o(s9_adr),.s9_sel_o(s9_sel),.s9_we_o(s9_we),.s9_cyc_o(s9_cyc),.s9_stb_o(s9_stb), - .s9_dat_i(s9_dat_miso),.s9_ack_i(s9_ack),.s9_err_i(0),.s9_rty_i(0), - .sa_dat_o(sa_dat_mosi),.sa_adr_o(sa_adr),.sa_sel_o(sa_sel),.sa_we_o(sa_we),.sa_cyc_o(sa_cyc),.sa_stb_o(sa_stb), - .sa_dat_i(sa_dat_miso),.sa_ack_i(sa_ack),.sa_err_i(0),.sa_rty_i(0), - .sb_dat_o(sb_dat_mosi),.sb_adr_o(sb_adr),.sb_sel_o(sb_sel),.sb_we_o(sb_we),.sb_cyc_o(sb_cyc),.sb_stb_o(sb_stb), - .sb_dat_i(sb_dat_miso),.sb_ack_i(sb_ack),.sb_err_i(0),.sb_rty_i(0), - .sc_dat_o(sc_dat_mosi),.sc_adr_o(sc_adr),.sc_sel_o(sc_sel),.sc_we_o(sc_we),.sc_cyc_o(sc_cyc),.sc_stb_o(sc_stb), - .sc_dat_i(sc_dat_miso),.sc_ack_i(sc_ack),.sc_err_i(0),.sc_rty_i(0), - .sd_dat_o(sd_dat_mosi),.sd_adr_o(sd_adr),.sd_sel_o(sd_sel),.sd_we_o(sd_we),.sd_cyc_o(sd_cyc),.sd_stb_o(sd_stb), - .sd_dat_i(sd_dat_miso),.sd_ack_i(sd_ack),.sd_err_i(0),.sd_rty_i(0), - .se_dat_o(se_dat_mosi),.se_adr_o(se_adr),.se_sel_o(se_sel),.se_we_o(se_we),.se_cyc_o(se_cyc),.se_stb_o(se_stb), - .se_dat_i(se_dat_miso),.se_ack_i(se_ack),.se_err_i(0),.se_rty_i(0), - .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 s1_ack = 0; assign s4_ack = 0; assign s5_ack = 0; assign s6_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; - - // ///////////////////////////////////////////////////////////////////////////////////// - // Slave 0, Misc LEDs, Switches, controls - - localparam REG_CGEN_CTRL = 7'd4; // out - localparam REG_CGEN_ST = 7'd6; // in - localparam REG_TEST = 7'd8; // out - localparam REG_RX_FRAMELEN = 7'd10; // in - localparam REG_TX_FRAMELEN = 7'd12; // out - - always @(posedge wb_clk) - if(wb_rst) - begin - reg_cgen_ctrl <= 2'b11; - reg_test <= 0; - frames_per_packet <= 0; - end - else - if(s0_cyc & s0_stb & s0_we) - case(s0_adr[6:0]) - REG_CGEN_CTRL : - reg_cgen_ctrl <= s0_dat_mosi; - REG_TEST : - reg_test <= s0_dat_mosi; - REG_RX_FRAMELEN : - frames_per_packet <= s0_dat_mosi[7:0]; - endcase // case (s0_adr[6:0]) - - assign debug_led = {run_tx, (run_rx0 | run_rx1), cgen_st_ld}; - assign { cgen_sync_b, cgen_ref_sel } = reg_cgen_ctrl; - - assign s0_dat_miso = (s0_adr[6:0] == REG_CGEN_CTRL) ? reg_cgen_ctrl : - (s0_adr[6:0] == REG_CGEN_ST) ? {13'b0,cgen_st_status,cgen_st_ld,cgen_st_refmon} : - (s0_adr[6:0] == REG_TEST) ? reg_test : - 16'hBEEF; - - assign s0_ack = s0_stb & s0_cyc; - - // ///////////////////////////////////////////////////////////////////////////////////// - // Slave 2, SPI - - spi_top16 shared_spi - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.wb_adr_i(s2_adr[4:0]),.wb_dat_i(s2_dat_mosi), - .wb_dat_o(s2_dat_miso),.wb_sel_i(s2_sel),.wb_we_i(s2_we),.wb_stb_i(s2_stb), - .wb_cyc_i(s2_cyc),.wb_ack_o(s2_ack),.wb_err_o(),.wb_int_o(), - .ss_pad_o(sen), .sclk_pad_o(sclk), .mosi_pad_o(mosi), .miso_pad_i(miso) ); - - // ///////////////////////////////////////////////////////////////////////// - // Slave 3, I2C - - wire scl_pad_i, scl_pad_o, scl_pad_oen_o, sda_pad_i, sda_pad_o, sda_pad_oen_o; - i2c_master_top #(.ARST_LVL(1)) i2c - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.arst_i(1'b0), - .wb_adr_i(s3_adr[3:1]),.wb_dat_i(s3_dat_mosi[7:0]),.wb_dat_o(s3_dat_miso[7:0]), - .wb_we_i(s3_we),.wb_stb_i(s3_stb),.wb_cyc_i(s3_cyc), - .wb_ack_o(s3_ack),.wb_inta_o(), - .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_pad_oen_o), - .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_pad_oen_o) ); - - assign s3_dat_miso[15:8] = 8'd0; - - // I2C -- Don't use external transistors for open drain, the FPGA implements this - IOBUF scl_pin(.O(scl_pad_i), .IO(db_scl), .I(scl_pad_o), .T(scl_pad_oen_o)); - IOBUF sda_pin(.O(sda_pad_i), .IO(db_sda), .I(sda_pad_o), .T(sda_pad_oen_o)); - - // ///////////////////////////////////////////////////////////////////////// - // GPIOs - - wire [31:0] gpio_readback; - - gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) - gpio_atr(.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .rx(run_rx0 | run_rx1), .tx(run_tx), - .gpio({io_tx,io_rx}), .gpio_readback(gpio_readback) ); - - // ///////////////////////////////////////////////////////////////////////// - // Settings Bus -- Slave #8 + 9 - - // only have 64 regs, 32 bits each with current setup... - settings_bus_16LE #(.AWIDTH(11),.RWIDTH(8)) 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) ); - - user_settings #(.BASE(SR_USER_REGS)) user_settings - (.clk(wb_clk),.rst(wb_rst),.set_stb(set_stb), - .set_addr(set_addr),.set_data(set_data), - .set_addr_user(set_addr_user),.set_data_user(set_data_user), - .set_stb_user(set_stb_user) ); - - // ///////////////////////////////////////////////////////////////////////// - // Readback mux 32 -- Slave #7 - - //compatibility number -> increment when the fpga has been sufficiently altered - localparam compat_num = {16'd9, 16'd3}; //major, minor - - 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({5'b0,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(reg_test32), .word05(32'b0), - .word06(compat_num), .word07(gpio_readback), - .word08(32'b0), .word09(32'b0), - .word10(32'b0), .word11(32'b0), - .word12(32'b0), .word13(32'b0), - .word14(32'b0), .word15(32'b0) - ); - - // ///////////////////////////////////////////////////////////////////////// - // VITA Timing - - time_64bit #(.BASE(SR_TIME64)) time_64bit - (.clk(wb_clk), .rst(wb_rst), .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), - .pps(pps_in), .vita_time(vita_time), .vita_time_pps(vita_time_pps), .pps_int(pps_int), - .exp_time_in(0)); - +`endif // ///////////////////////////////////////////////////////////////////////////////////// // Debug circuitry - assign debug_clk = 2'b00; // { gpif_clk, clk_fpga }; - assign debug = 0; - + assign debug_clk = 2'b11; + assign debug = 32'hffffffff; + endmodule // u1plus_core diff --git a/fpga/usrp2/top/E1x0/u1e.ucf b/fpga/usrp2/top/E1x0/E1x0.ucf index 278fc289a..278fc289a 100644 --- a/fpga/usrp2/top/E1x0/u1e.ucf +++ b/fpga/usrp2/top/E1x0/E1x0.ucf diff --git a/fpga/usrp2/top/E1x0/u1e.v b/fpga/usrp2/top/E1x0/E1x0.v index 903ef7a6f..e7b0a4e00 100644 --- a/fpga/usrp2/top/E1x0/u1e.v +++ b/fpga/usrp2/top/E1x0/E1x0.v @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -18,7 +18,7 @@ `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// -module u1e +module E1x0 (input CLK_FPGA_P, input CLK_FPGA_N, // Diff output [3:0] debug_led, output [31:0] debug, output [1:0] debug_clk, input debug_pb, output FPGA_TXD, input FPGA_RXD, @@ -52,13 +52,23 @@ module u1e input PPS_IN ); + assign FPGA_TXD = 0; //dont care + // ///////////////////////////////////////////////////////////////////////// // Clocking - wire clk_fpga; + wire clk_fpga; + wire reset; + + reg async_reset; + always @(negedge EM_CLK) begin + async_reset <= ~EM_NCS6 && ~EM_NWE && (EM_A[9:2] == 8'hff) && EM_D[0]; + end IBUFGDS #(.IOSTANDARD("LVDS_33"), .DIFF_TERM("TRUE")) clk_fpga_pin (.O(clk_fpga),.I(CLK_FPGA_P),.IB(CLK_FPGA_N)); + reset_sync reset_sync(.clk(clk_fpga), .reset_in(async_reset), .reset_out(reset)); + // ///////////////////////////////////////////////////////////////////////// // UART level conversion assign fpga_txd1 = overo_txd1; @@ -73,12 +83,12 @@ module u1e assign miso = (~db_sen_tx & db_miso_tx) | (~db_sen_rx & db_miso_rx) | (~sen_codec & miso_codec) | (~cgen_sen_b & cgen_miso); - //assign the aux spi to the cgen (bypasses wishbone) + //assign the aux spi to the cgen (bypasses control fifo) assign cgen_sclk = overo_gpio65; assign cgen_sen_b = overo_gpio128; assign cgen_mosi = overo_gpio145; - wire proc_int; //re-purpose gpio for interrupt when we are not using aux spi - assign overo_gpio147 = (cgen_sen_b == 1'b0)? cgen_miso : proc_int; + wire has_resp; //re-purpose gpio for interrupt when we are not using aux spi + assign overo_gpio147 = (cgen_sen_b == 1'b0)? cgen_miso : has_resp; wire _cgen_sen_b; //assign cgen_sen_b = _cgen_sen_b; //replaced by aux spi @@ -121,29 +131,66 @@ module u1e .D1(1'b1), // 1-bit data input (associated with C1) .R(1'b0), // 1-bit reset input .S(1'b0)); // 1-bit set input - + // ///////////////////////////////////////////////////////////////////////// - // Main U1E Core - u1e_core u1e_core(.clk_fpga(clk_fpga), .rst_fpga(~debug_pb), - .debug_led(debug_led), .debug(debug), .debug_clk(debug_clk), - .debug_txd(FPGA_TXD), .debug_rxd(FPGA_RXD), - .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), - .EM_WAIT0(EM_WAIT0), .EM_NCS4(EM_NCS4), .EM_NCS5(EM_NCS5), - .EM_NCS6(EM_NCS6), .EM_NWE(EM_NWE), .EM_NOE(EM_NOE), - .db_sda(db_sda), .db_scl(db_scl), - .sclk(sclk), .sen({_cgen_sen_b,sen_codec,db_sen_tx,db_sen_rx}), .mosi(mosi), .miso(miso), - .cgen_st_status(cgen_st_status), .cgen_st_ld(cgen_st_ld),.cgen_st_refmon(cgen_st_refmon), - .cgen_sync_b(cgen_sync_b), .cgen_ref_sel(cgen_ref_sel), - .tx_have_space(overo_gpio144), - .rx_have_data(overo_gpio146), - .io_tx(io_tx), .io_rx(io_rx), - .tx_i(tx_i), .tx_q(tx_q), - .rx_i(DA), .rx_q(DB), - .pps_in(PPS_IN), .proc_int(proc_int) ); + // Main Core + wire [35:0] rx_data, tx_data, ctrl_data, resp_data; + wire rx_src_rdy, rx_dst_rdy, tx_src_rdy, tx_dst_rdy, resp_src_rdy, resp_dst_rdy, ctrl_src_rdy, ctrl_dst_rdy; + wire dsp_rx_run, dsp_tx_run; + wire [7:0] sen8; + assign {_cgen_sen_b,sen_codec,db_sen_tx,db_sen_rx} = sen8[3:0]; + wire [31:0] core_debug; + + assign debug_led = ~{PPS_IN, dsp_tx_run, dsp_rx_run, cgen_st_ld}; + wire cgen_sync; + assign { cgen_sync_b, cgen_ref_sel } = {~cgen_sync, 1'b1}; + + u1plus_core #( + .NUM_RX_DSPS(2), + .DSP_RX_XTRA_FIFOSIZE(10), + .DSP_TX_XTRA_FIFOSIZE(10), + .USE_PACKET_PADDER(0) + ) core( + .clk(clk_fpga), .reset(reset), + .debug(core_debug), .debug_clk(debug_clk), + + .rx_data(rx_data), .rx_src_rdy(rx_src_rdy), .rx_dst_rdy(rx_dst_rdy), + .tx_data(tx_data), .tx_src_rdy(tx_src_rdy), .tx_dst_rdy(tx_dst_rdy), + .ctrl_data(ctrl_data), .ctrl_src_rdy(ctrl_src_rdy), .ctrl_dst_rdy(ctrl_dst_rdy), + .resp_data(resp_data), .resp_src_rdy(resp_src_rdy), .resp_dst_rdy(resp_dst_rdy), + + .dsp_rx_run(dsp_rx_run), .dsp_tx_run(dsp_tx_run), + .clock_sync(cgen_sync), + + .db_sda(db_sda), .db_scl(db_scl), + .sclk(sclk), .sen(sen8), .mosi(mosi), .miso(miso), + .io_tx(io_tx), .io_rx(io_rx), + .tx_i(tx_i), .tx_q(tx_q), + .rx_i(DA), .rx_q(DB), + .pps_in(PPS_IN) ); // ///////////////////////////////////////////////////////////////////////// - // Local Debug - // assign debug_clk = {clk_fpga, clk_2x }; - // assign debug = { TXSYNC, TXBLANK, TX }; - -endmodule // u1e + // Interface between GPMC/host + wire [31:0] gpmc_debug; + + gpmc #(.TXFIFOSIZE(13), .RXFIFOSIZE(13)) + gpmc (.arst(async_reset), + .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), + .EM_WAIT0(EM_WAIT0), .EM_NCS4(EM_NCS4), .EM_NCS6(EM_NCS6), .EM_NWE(EM_NWE), + .EM_NOE(EM_NOE), + + .rx_have_data(overo_gpio146), .tx_have_space(overo_gpio144), + .resp_have_data(has_resp), + + .fifo_clk(clk_fpga), .fifo_rst(reset), + .rx_data(rx_data), .rx_src_rdy(rx_src_rdy), .rx_dst_rdy(rx_dst_rdy), + .tx_data(tx_data), .tx_src_rdy(tx_src_rdy), .tx_dst_rdy(tx_dst_rdy), + .ctrl_data(ctrl_data), .ctrl_src_rdy(ctrl_src_rdy), .ctrl_dst_rdy(ctrl_dst_rdy), + .resp_data(resp_data), .resp_src_rdy(resp_src_rdy), .resp_dst_rdy(resp_dst_rdy), + + .debug(gpmc_debug)); + + //assign debug = gpmc_debug; + assign debug = core_debug; + +endmodule // E1x0 diff --git a/fpga/usrp2/top/E1x0/Makefile.E100 b/fpga/usrp2/top/E1x0/Makefile.E100 index 3ba7e1031..92334d987 100644 --- a/fpga/usrp2/top/E1x0/Makefile.E100 +++ b/fpga/usrp2/top/E1x0/Makefile.E100 @@ -5,7 +5,7 @@ ################################################## # Project Setup ################################################## -TOP_MODULE = u1e +TOP_MODULE = E1x0 BUILD_DIR = $(abspath build$(ISE)-E100) # set me in a custom makefile @@ -48,9 +48,9 @@ simulator "ISE Simulator (VHDL/Verilog)" \ # Sources ################################################## TOP_SRCS = \ -u1e_core.v \ -u1e.v \ -u1e.ucf \ +../B100/u1plus_core.v \ +E1x0.v \ +E1x0.ucf \ timing.ucf SOURCES = $(abspath $(TOP_SRCS)) $(FIFO_SRCS) \ diff --git a/fpga/usrp2/top/E1x0/Makefile.E110 b/fpga/usrp2/top/E1x0/Makefile.E110 index 89e51b523..e5be8d2fa 100644 --- a/fpga/usrp2/top/E1x0/Makefile.E110 +++ b/fpga/usrp2/top/E1x0/Makefile.E110 @@ -5,7 +5,7 @@ ################################################## # Project Setup ################################################## -TOP_MODULE = u1e +TOP_MODULE = E1x0 BUILD_DIR = $(abspath build$(ISE)-E110) # set me in a custom makefile @@ -48,9 +48,9 @@ simulator "ISE Simulator (VHDL/Verilog)" \ # Sources ################################################## TOP_SRCS = \ -u1e_core.v \ -u1e.v \ -u1e.ucf \ +../B100/u1plus_core.v \ +E1x0.v \ +E1x0.ucf \ timing.ucf SOURCES = $(abspath $(TOP_SRCS)) $(FIFO_SRCS) \ diff --git a/fpga/usrp2/top/E1x0/core_compile b/fpga/usrp2/top/E1x0/core_compile index 14e138fa3..ab992f29d 100755 --- a/fpga/usrp2/top/E1x0/core_compile +++ b/fpga/usrp2/top/E1x0/core_compile @@ -1,3 +1,3 @@ -iverilog -Wall -y. -y ../../control_lib/ -y ../../custom/ -y ../../fifo/ -y ../../gpmc/ -y ../../models/ -y ../../sdr_lib/ -y ../../coregen/ -y ../../vrt/ -y ../../opencores/i2c/rtl/verilog/ -y ../../opencores/spi/rtl/verilog/ -y ../../timing/ -y ../../opencores/8b10b/ -I ../../opencores/spi/rtl/verilog/ -I ../../opencores/i2c/rtl/verilog/ -y ../../simple_gemac -y $XILINX/verilog/src/unisims u1e_core.v 2>&1 | grep -v timescale | grep -v coregen | grep -v models +iverilog -Wall -y. -y ../../control_lib/ -y ../../custom/ -y ../../fifo/ -y ../../gpmc/ -y ../../models/ -y ../../sdr_lib/ -y ../../coregen/ -y ../../vrt/ -y ../../opencores/i2c/rtl/verilog/ -y ../../opencores/spi/rtl/verilog/ -y ../../timing/ -y ../../opencores/8b10b/ -I ../../opencores/spi/rtl/verilog/ -I ../../opencores/i2c/rtl/verilog/ -y ../B100 -y $XILINX/verilog/src/unisims E1x0.v 2>&1 | grep -v timescale | grep -v coregen | grep -v models diff --git a/fpga/usrp2/top/E1x0/timing.ucf b/fpga/usrp2/top/E1x0/timing.ucf index 47c250c2f..7d3d9e090 100644 --- a/fpga/usrp2/top/E1x0/timing.ucf +++ b/fpga/usrp2/top/E1x0/timing.ucf @@ -3,25 +3,21 @@ NET "CLK_FPGA_P" TNM_NET = "CLK_FPGA_P"; TIMESPEC "TS_clk_fpga_p" = PERIOD "CLK_FPGA_P" 15625 ps HIGH 50 %; NET "EM_CLK" TNM_NET = "EM_CLK"; -TIMESPEC "TS_em_clk" = PERIOD "EM_CLK" 12048 ps HIGH 50 %; +TIMESPEC "TS_em_clk" = PERIOD "EM_CLK" 18867 ps HIGH 50 %; #constrain GPMC IO -NET "EM_D<*>" MAXDELAY = 5.5 ns; -NET "EM_A<*>" MAXDELAY = 5.5 ns; -NET "EM_NBE<*>" MAXDELAY = 5.5 ns; -NET "EM_NCS4" MAXDELAY = 5.5 ns; -NET "EM_NCS6" MAXDELAY = 5.5 ns; -NET "EM_NWE" MAXDELAY = 5.5 ns; -NET "EM_NOE" MAXDELAY = 5.5 ns; +INST "EM_D<*>" TNM = gpmc_net_out; +INST "EM_D<*>" TNM = gpmc_net; +INST "EM_A<*>" TNM = gpmc_net; +INST "EM_NCS4" TNM = gpmc_net; +INST "EM_NCS6" TNM = gpmc_net; +INST "EM_NWE" TNM = gpmc_net; +INST "EM_NOE" TNM = gpmc_net; + +TIMEGRP "gpmc_net" OFFSET = IN 6 ns VALID 12 ns BEFORE "EM_CLK" FALLING; +TIMEGRP "gpmc_net_out" OFFSET = OUT 14 ns AFTER "EM_CLK" RISING; //2 clock cyc per read #constrain interrupt lines NET "overo_gpio144" MAXDELAY = 5.5 ns; #have space NET "overo_gpio146" MAXDELAY = 5.5 ns; #have data NET "overo_gpio147" MAXDELAY = 5.5 ns; #have msg/aux spi miso - -#NET "adc_a<*>" TNM_NET = ADC_DATA_GRP; -#NET "adc_b<*>" TNM_NET = ADC_DATA_GRP; -#TIMEGRP "ADC_DATA_GRP" OFFSET = IN 1 ns VALID 5 ns BEFORE "clk_fpga_p" RISING; - -#NET "adc_a<*>" OFFSET = IN 1 ns VALID 5 ns BEFORE "clk_fpga_p" RISING; -#NET "adc_b<*>" OFFSET = IN 1 ns VALID 5 ns BEFORE "clk_fpga_p" RISING; diff --git a/fpga/usrp2/top/E1x0/u1e_core.v b/fpga/usrp2/top/E1x0/u1e_core.v deleted file mode 100644 index bd19d6076..000000000 --- a/fpga/usrp2/top/E1x0/u1e_core.v +++ /dev/null @@ -1,494 +0,0 @@ -// -// Copyright 2011-2012 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/>. -// - - - -module u1e_core - (input clk_fpga, input rst_fpga, - output [3:0] debug_led, output [31:0] debug, output [1:0] debug_clk, - output debug_txd, input debug_rxd, - - // GPMC - input EM_CLK, inout [15:0] EM_D, input [10:1] EM_A, input [1:0] EM_NBE, - input EM_WAIT0, input EM_NCS4, input EM_NCS5, input EM_NCS6, - input EM_NWE, input EM_NOE, - - inout db_sda, inout db_scl, - output sclk, output [15:0] sen, output mosi, input miso, - - input cgen_st_status, input cgen_st_ld, input cgen_st_refmon, output cgen_sync_b, output cgen_ref_sel, - output tx_have_space, output rx_have_data, - inout [15:0] io_tx, inout [15:0] io_rx, - output [13:0] tx_i, output [13:0] tx_q, - input [11:0] rx_i, input [11:0] rx_q, - - input pps_in, output reg proc_int - ); - - localparam TXFIFOSIZE = 13; - localparam RXFIFOSIZE = 13; - - // 64 total regs in address space - localparam SR_RX_CTRL0 = 0; // 9 regs (+0 to +8) - localparam SR_RX_DSP0 = 10; // 4 regs (+0 to +3) - localparam SR_RX_CTRL1 = 16; // 9 regs (+0 to +8) - localparam SR_RX_DSP1 = 26; // 4 regs (+0 to +3) - localparam SR_ERR_CTRL = 30; // 1 reg - localparam SR_TX_CTRL = 32; // 4 regs (+0 to +3) - localparam SR_TX_DSP = 38; // 3 regs (+0 to +2) - - localparam SR_TIME64 = 42; // 6 regs (+0 to +5) - localparam SR_RX_FRONT = 48; // 5 regs (+0 to +4) - localparam SR_TX_FRONT = 54; // 5 regs (+0 to +4) - - localparam SR_REG_TEST32 = 60; // 1 reg - localparam SR_CLEAR_FIFO = 61; // 1 reg - localparam SR_GLOBAL_RESET = 63; // 1 reg - localparam SR_USER_REGS = 64; // 2 regs - - localparam SR_GPIO = 128; // 5 regs - - wire wb_clk = clk_fpga; - wire wb_rst, global_reset; - - wire pps_int; - wire [63:0] vita_time, vita_time_pps; - reg [15:0] reg_cgen_ctrl, reg_test, xfer_rate; - wire [7:0] test_rate; - wire [3:0] test_ctrl; - - wire [7:0] set_addr, set_addr_user; - wire [31:0] set_data, set_data_user; - wire set_stb, set_stb_user; - - wire [31:0] debug_vt; - wire rx_overrun_dsp0, rx_overrun_dsp1, rx_overrun_gpmc, tx_underrun_dsp, tx_underrun_gpmc; - wire rx_overrun = rx_overrun_gpmc | rx_overrun_dsp0 | rx_overrun_dsp1; - wire tx_underrun = tx_underrun_gpmc | tx_underrun_dsp; - - 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; - localparam aw = 11; - localparam sw = 2; - - wire [dw-1:0] m0_dat_mosi, m0_dat_miso; - wire [aw-1:0] m0_adr; - wire [sw-1:0] m0_sel; - wire m0_cyc, m0_stb, m0_we, m0_ack, m0_err, m0_rty; - - wire [31:0] debug_gpmc; - - wire [35:0] tx_data, rx_data, tx_err_data; - wire tx_src_rdy, tx_dst_rdy, rx_src_rdy, rx_dst_rdy, - tx_err_src_rdy, tx_err_dst_rdy; - - wire clear_fifo; - - setting_reg #(.my_addr(SR_CLEAR_FIFO), .width(1)) sr_clear_fifo - (.clk(wb_clk),.rst(wb_rst),.strobe(set_stb),.addr(set_addr), - .in(set_data),.out(),.changed(clear_fifo)); - - wire run_rx0, run_rx1; - - gpmc #(.TXFIFOSIZE(TXFIFOSIZE), .RXFIFOSIZE(RXFIFOSIZE)) - gpmc (.arst(wb_rst), - .EM_CLK(EM_CLK), .EM_D(EM_D), .EM_A(EM_A), .EM_NBE(EM_NBE), - .EM_WAIT0(EM_WAIT0), .EM_NCS4(EM_NCS4), .EM_NCS6(EM_NCS6), .EM_NWE(EM_NWE), - .EM_NOE(EM_NOE), - - .rx_have_data(rx_have_data), .tx_have_space(tx_have_space), - - .wb_clk(wb_clk), .wb_rst(wb_rst), - .wb_adr_o(m0_adr), .wb_dat_mosi(m0_dat_mosi), .wb_dat_miso(m0_dat_miso), - .wb_sel_o(m0_sel), .wb_cyc_o(m0_cyc), .wb_stb_o(m0_stb), .wb_we_o(m0_we), - .wb_ack_i(m0_ack), - - .fifo_clk(wb_clk), .fifo_rst(wb_rst), .clear_tx(clear_fifo), .clear_rx(clear_fifo), - .tx_data_o(tx_data), .tx_src_rdy_o(tx_src_rdy), .tx_dst_rdy_i(tx_dst_rdy), - .rx_data_i(rx_data), .rx_src_rdy_i(rx_src_rdy), .rx_dst_rdy_o(rx_dst_rdy), - - .tx_underrun(tx_underrun_gpmc), .rx_overrun(rx_overrun_gpmc), - - .test_rate(test_rate), .test_ctrl(test_ctrl), - .debug(debug_gpmc)); - - wire rx_src_rdy_int, rx_dst_rdy_int, tx_src_rdy_int, tx_dst_rdy_int; - - wire [31:0] debug_rx_dsp, vrc_debug, vrf_debug, vr_debug; - - // ///////////////////////////////////////////////////////////////////////// - // RX ADC Frontend, does IQ Balance, DC Offset, muxing - - wire [23:0] rx_fe_i, rx_fe_q; // 24 bits is total overkill here, but it matches u2/u2p - - rx_frontend #(.BASE(SR_RX_FRONT)) rx_frontend - (.clk(wb_clk),.rst(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .adc_a({rx_i,4'b00}),.adc_ovf_a(0), - .adc_b({rx_q,4'b00}),.adc_ovf_b(0), - .i_out(rx_fe_i), .q_out(rx_fe_q), .run(run_rx0 | run_rx1), .debug()); - - // ///////////////////////////////////////////////////////////////////////// - // DSP RX 0 - - wire [31:0] sample_rx0; - wire strobe_rx0, clear_rx0; - wire [35:0] vita_rx_data0; - wire vita_rx_src_rdy0, vita_rx_dst_rdy0; - - ddc_chain #(.BASE(SR_RX_DSP0), .DSPNO(0)) ddc_chain0 - (.clk(wb_clk), .rst(wb_rst), .clr(clear_rx0), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), - .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), - .debug() ); - - vita_rx_chain #(.BASE(SR_RX_CTRL0), .UNIT(0), .FIFOSIZE(10), .PROT_ENG_FLAGS(0), .DSP_NUMBER(0)) vita_rx_chain0 - (.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .vita_time(vita_time), .overrun(rx_overrun_dsp0), - .sample(sample_rx0), .run(run_rx0), .strobe(strobe_rx0), .clear_o(clear_rx0), - .rx_data_o(vita_rx_data0), .rx_dst_rdy_i(vita_rx_dst_rdy0), .rx_src_rdy_o(vita_rx_src_rdy0), - .debug() ); - - // ///////////////////////////////////////////////////////////////////////// - // DSP RX 1 - - wire [31:0] sample_rx1; - wire strobe_rx1, clear_rx1; - wire [35:0] vita_rx_data1; - wire vita_rx_src_rdy1, vita_rx_dst_rdy1; - - ddc_chain #(.BASE(SR_RX_DSP1), .DSPNO(1)) ddc_chain1 - (.clk(wb_clk),.rst(wb_rst), .clr(clear_rx1), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .rx_fe_i(rx_fe_i),.rx_fe_q(rx_fe_q), - .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), - .debug() ); - - vita_rx_chain #(.BASE(SR_RX_CTRL1), .UNIT(1), .FIFOSIZE(10), .PROT_ENG_FLAGS(0), .DSP_NUMBER(1)) vita_rx_chain1 - (.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .vita_time(vita_time), .overrun(rx_overrun_dsp1), - .sample(sample_rx1), .run(run_rx1), .strobe(strobe_rx1), .clear_o(clear_rx1), - .rx_data_o(vita_rx_data1), .rx_dst_rdy_i(vita_rx_dst_rdy1), .rx_src_rdy_o(vita_rx_src_rdy1), - .debug() ); - - // ///////////////////////////////////////////////////////////////////////// - // RX Stream muxing - - fifo36_mux #(.prio(0)) mux_data_streams - (.clk(wb_clk), .reset(wb_rst), .clear(clear_fifo), - .data0_i(vita_rx_data0), .src0_rdy_i(vita_rx_src_rdy0), .dst0_rdy_o(vita_rx_dst_rdy0), - .data1_i(vita_rx_data1), .src1_rdy_i(vita_rx_src_rdy1), .dst1_rdy_o(vita_rx_dst_rdy1), - .data_o(rx_data), .src_rdy_o(rx_src_rdy), .dst_rdy_i(rx_dst_rdy)); - - // /////////////////////////////////////////////////////////////////////////////////// - // DSP TX - - wire run_tx; - wire [23:0] tx_fe_i, tx_fe_q; - wire [31:0] sample_tx; - wire strobe_tx, clear_tx; - - vita_tx_chain #(.BASE(SR_TX_CTRL), .FIFOSIZE(10), .POST_ENGINE_FIFOSIZE(11), - .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), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .vita_time(vita_time), - .tx_data_i(tx_data), .tx_src_rdy_i(tx_src_rdy), .tx_dst_rdy_o(tx_dst_rdy), - .err_data_o(tx_err_data), .err_src_rdy_o(tx_err_src_rdy), .err_dst_rdy_i(tx_err_dst_rdy), - .sample(sample_tx), .strobe(strobe_tx), - .underrun(tx_underrun_dsp), .run(run_tx), .clear_o(clear_tx), - .debug(debug_vt)); - - duc_chain #(.BASE(SR_TX_DSP), .DSPNO(0)) duc_chain - (.clk(wb_clk), .rst(wb_rst), .clr(clear_tx), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .set_stb_user(set_stb_user), .set_addr_user(set_addr_user), .set_data_user(set_data_user), - .tx_fe_i(tx_fe_i),.tx_fe_q(tx_fe_q), - .sample(sample_tx), .run(run_tx), .strobe(strobe_tx), - .debug() ); - - tx_frontend #(.BASE(SR_TX_FRONT), .WIDTH_OUT(14)) tx_frontend - (.clk(wb_clk), .rst(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .tx_i(tx_fe_i), .tx_q(tx_fe_q), .run(1'b1), - .dac_a(tx_i), .dac_b(tx_q)); - - // ///////////////////////////////////////////////////////////////////////////////////// - // Wishbone Intercon, single master - wire [dw-1:0] s0_dat_mosi, s1_dat_mosi, s0_dat_miso, s1_dat_miso, s2_dat_mosi, s3_dat_mosi, s2_dat_miso, s3_dat_miso, - s4_dat_mosi, s5_dat_mosi, s4_dat_miso, s5_dat_miso, s6_dat_mosi, s7_dat_mosi, s6_dat_miso, s7_dat_miso, - s8_dat_mosi, s9_dat_mosi, s8_dat_miso, s9_dat_miso, sa_dat_mosi, sb_dat_mosi, sa_dat_miso, sb_dat_miso, - sc_dat_mosi, sd_dat_mosi, sc_dat_miso, sd_dat_miso, se_dat_mosi, sf_dat_mosi, se_dat_miso, sf_dat_miso; - wire [aw-1:0] s0_adr,s1_adr,s2_adr,s3_adr,s4_adr,s5_adr,s6_adr,s7_adr; - wire [aw-1:0] s8_adr,s9_adr,sa_adr,sb_adr,sc_adr, sd_adr, se_adr, sf_adr; - wire [sw-1:0] s0_sel,s1_sel,s2_sel,s3_sel,s4_sel,s5_sel,s6_sel,s7_sel; - wire [sw-1:0] s8_sel,s9_sel,sa_sel,sb_sel,sc_sel, sd_sel, se_sel, sf_sel; - wire s0_ack,s1_ack,s2_ack,s3_ack,s4_ack,s5_ack,s6_ack,s7_ack; - wire s8_ack,s9_ack,sa_ack,sb_ack,sc_ack, sd_ack, se_ack, sf_ack; - wire s0_stb,s1_stb,s2_stb,s3_stb,s4_stb,s5_stb,s6_stb,s7_stb; - wire s8_stb,s9_stb,sa_stb,sb_stb,sc_stb, sd_stb, se_stb, sf_stb; - wire s0_cyc,s1_cyc,s2_cyc,s3_cyc,s4_cyc,s5_cyc,s6_cyc,s7_cyc; - wire s8_cyc,s9_cyc,sa_cyc,sb_cyc,sc_cyc, sd_cyc, se_cyc, sf_cyc; - wire s0_we,s1_we,s2_we,s3_we,s4_we,s5_we,s6_we,s7_we; - wire s8_we,s9_we,sa_we,sb_we,sc_we,sd_we, se_we, sf_we; - - wb_1master #(.dw(dw), .aw(aw), .sw(sw), .decode_w(4), - .s0_addr(4'h0), .s0_mask(4'hF), // Misc Regs - .s1_addr(4'h1), .s1_mask(4'hF), // Unused - .s2_addr(4'h2), .s2_mask(4'hF), // SPI - .s3_addr(4'h3), .s3_mask(4'hF), // I2C - .s4_addr(4'h4), .s4_mask(4'hF), // Unused - .s5_addr(4'h5), .s5_mask(4'hF), // Unused on B1x0, Async Msg on E1x0 - .s6_addr(4'h6), .s6_mask(4'hF), // Unused - .s7_addr(4'h7), .s7_mask(4'hF), // Readback MUX - .s8_addr(4'h8), .s8_mask(4'h8), // Setting Regs -- slave 8 is 8 slaves wide - // slaves 9-f alias to slave 1, all are unused - .s9_addr(4'h1), .s9_mask(4'hF), - .sa_addr(4'h1), .sa_mask(4'hF), .sb_addr(4'h1), .sb_mask(4'hF), - .sc_addr(4'h1), .sc_mask(4'hF), .sd_addr(4'h1), .sd_mask(4'hF), - .se_addr(4'h1), .se_mask(4'hF), .sf_addr(4'h1), .sf_mask(4'hF)) - wb_1master - (.clk_i(wb_clk),.rst_i(wb_rst), - .m0_dat_o(m0_dat_miso),.m0_ack_o(m0_ack),.m0_err_o(m0_err),.m0_rty_o(m0_rty),.m0_dat_i(m0_dat_mosi), - .m0_adr_i(m0_adr),.m0_sel_i(m0_sel),.m0_we_i(m0_we),.m0_cyc_i(m0_cyc),.m0_stb_i(m0_stb), - .s0_dat_o(s0_dat_mosi),.s0_adr_o(s0_adr),.s0_sel_o(s0_sel),.s0_we_o(s0_we),.s0_cyc_o(s0_cyc),.s0_stb_o(s0_stb), - .s0_dat_i(s0_dat_miso),.s0_ack_i(s0_ack),.s0_err_i(0),.s0_rty_i(0), - .s1_dat_o(s1_dat_mosi),.s1_adr_o(s1_adr),.s1_sel_o(s1_sel),.s1_we_o(s1_we),.s1_cyc_o(s1_cyc),.s1_stb_o(s1_stb), - .s1_dat_i(s1_dat_miso),.s1_ack_i(s1_ack),.s1_err_i(0),.s1_rty_i(0), - .s2_dat_o(s2_dat_mosi),.s2_adr_o(s2_adr),.s2_sel_o(s2_sel),.s2_we_o(s2_we),.s2_cyc_o(s2_cyc),.s2_stb_o(s2_stb), - .s2_dat_i(s2_dat_miso),.s2_ack_i(s2_ack),.s2_err_i(0),.s2_rty_i(0), - .s3_dat_o(s3_dat_mosi),.s3_adr_o(s3_adr),.s3_sel_o(s3_sel),.s3_we_o(s3_we),.s3_cyc_o(s3_cyc),.s3_stb_o(s3_stb), - .s3_dat_i(s3_dat_miso),.s3_ack_i(s3_ack),.s3_err_i(0),.s3_rty_i(0), - .s4_dat_o(s4_dat_mosi),.s4_adr_o(s4_adr),.s4_sel_o(s4_sel),.s4_we_o(s4_we),.s4_cyc_o(s4_cyc),.s4_stb_o(s4_stb), - .s4_dat_i(s4_dat_miso),.s4_ack_i(s4_ack),.s4_err_i(0),.s4_rty_i(0), - .s5_dat_o(s5_dat_mosi),.s5_adr_o(s5_adr),.s5_sel_o(s5_sel),.s5_we_o(s5_we),.s5_cyc_o(s5_cyc),.s5_stb_o(s5_stb), - .s5_dat_i(s5_dat_miso),.s5_ack_i(s5_ack),.s5_err_i(0),.s5_rty_i(0), - .s6_dat_o(s6_dat_mosi),.s6_adr_o(s6_adr),.s6_sel_o(s6_sel),.s6_we_o(s6_we),.s6_cyc_o(s6_cyc),.s6_stb_o(s6_stb), - .s6_dat_i(s6_dat_miso),.s6_ack_i(s6_ack),.s6_err_i(0),.s6_rty_i(0), - .s7_dat_o(s7_dat_mosi),.s7_adr_o(s7_adr),.s7_sel_o(s7_sel),.s7_we_o(s7_we),.s7_cyc_o(s7_cyc),.s7_stb_o(s7_stb), - .s7_dat_i(s7_dat_miso),.s7_ack_i(s7_ack),.s7_err_i(0),.s7_rty_i(0), - .s8_dat_o(s8_dat_mosi),.s8_adr_o(s8_adr),.s8_sel_o(s8_sel),.s8_we_o(s8_we),.s8_cyc_o(s8_cyc),.s8_stb_o(s8_stb), - .s8_dat_i(s8_dat_miso),.s8_ack_i(s8_ack),.s8_err_i(0),.s8_rty_i(0), - .s9_dat_o(s9_dat_mosi),.s9_adr_o(s9_adr),.s9_sel_o(s9_sel),.s9_we_o(s9_we),.s9_cyc_o(s9_cyc),.s9_stb_o(s9_stb), - .s9_dat_i(s9_dat_miso),.s9_ack_i(s9_ack),.s9_err_i(0),.s9_rty_i(0), - .sa_dat_o(sa_dat_mosi),.sa_adr_o(sa_adr),.sa_sel_o(sa_sel),.sa_we_o(sa_we),.sa_cyc_o(sa_cyc),.sa_stb_o(sa_stb), - .sa_dat_i(sa_dat_miso),.sa_ack_i(sa_ack),.sa_err_i(0),.sa_rty_i(0), - .sb_dat_o(sb_dat_mosi),.sb_adr_o(sb_adr),.sb_sel_o(sb_sel),.sb_we_o(sb_we),.sb_cyc_o(sb_cyc),.sb_stb_o(sb_stb), - .sb_dat_i(sb_dat_miso),.sb_ack_i(sb_ack),.sb_err_i(0),.sb_rty_i(0), - .sc_dat_o(sc_dat_mosi),.sc_adr_o(sc_adr),.sc_sel_o(sc_sel),.sc_we_o(sc_we),.sc_cyc_o(sc_cyc),.sc_stb_o(sc_stb), - .sc_dat_i(sc_dat_miso),.sc_ack_i(sc_ack),.sc_err_i(0),.sc_rty_i(0), - .sd_dat_o(sd_dat_mosi),.sd_adr_o(sd_adr),.sd_sel_o(sd_sel),.sd_we_o(sd_we),.sd_cyc_o(sd_cyc),.sd_stb_o(sd_stb), - .sd_dat_i(sd_dat_miso),.sd_ack_i(sd_ack),.sd_err_i(0),.sd_rty_i(0), - .se_dat_o(se_dat_mosi),.se_adr_o(se_adr),.se_sel_o(se_sel),.se_we_o(se_we),.se_cyc_o(se_cyc),.se_stb_o(se_stb), - .se_dat_i(se_dat_miso),.se_ack_i(se_ack),.se_err_i(0),.se_rty_i(0), - .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 s1_ack = 0; assign s4_ack = 0; assign s6_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; - - // ///////////////////////////////////////////////////////////////////////////////////// - // Slave 0, Misc LEDs, Switches, controls - - localparam REG_CGEN_CTRL = 7'd4; // out - localparam REG_CGEN_ST = 7'd6; // in - localparam REG_TEST = 7'd8; // out - localparam REG_XFER_RATE = 7'd14; // out - - always @(posedge wb_clk) - if(wb_rst) - begin - reg_cgen_ctrl <= 2'b11; - reg_test <= 0; - xfer_rate <= 0; - end - else - if(s0_cyc & s0_stb & s0_we) - case(s0_adr[6:0]) - REG_CGEN_CTRL : - reg_cgen_ctrl <= s0_dat_mosi; - REG_TEST : - reg_test <= s0_dat_mosi; - REG_XFER_RATE : - xfer_rate <= s0_dat_mosi; - endcase // case (s0_adr[6:0]) - - assign test_ctrl = xfer_rate[11:8]; - assign test_rate = xfer_rate[7:0]; - - assign { debug_led[3:0] } = ~{1'b1, run_tx, run_rx0 | run_rx1, cgen_st_ld}; - assign { cgen_sync_b, cgen_ref_sel } = reg_cgen_ctrl; - - assign s0_dat_miso = (s0_adr[6:0] == REG_CGEN_CTRL) ? reg_cgen_ctrl : - (s0_adr[6:0] == REG_CGEN_ST) ? {13'b0,cgen_st_status,cgen_st_ld,cgen_st_refmon} : - (s0_adr[6:0] == REG_TEST) ? reg_test : - 16'hBEEF; - - assign s0_ack = s0_stb & s0_cyc; - - // ///////////////////////////////////////////////////////////////////////////////////// - // Slave 2, SPI - - spi_top16 shared_spi - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.wb_adr_i(s2_adr[4:0]),.wb_dat_i(s2_dat_mosi), - .wb_dat_o(s2_dat_miso),.wb_sel_i(s2_sel),.wb_we_i(s2_we),.wb_stb_i(s2_stb), - .wb_cyc_i(s2_cyc),.wb_ack_o(s2_ack),.wb_err_o(),.wb_int_o(), - .ss_pad_o(sen), .sclk_pad_o(sclk), .mosi_pad_o(mosi), .miso_pad_i(miso) ); - - // ///////////////////////////////////////////////////////////////////////// - // Slave 3, I2C - - wire scl_pad_i, scl_pad_o, scl_pad_oen_o, sda_pad_i, sda_pad_o, sda_pad_oen_o; - i2c_master_top #(.ARST_LVL(1)) i2c - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.arst_i(1'b0), - .wb_adr_i(s3_adr[3:1]),.wb_dat_i(s3_dat_mosi[7:0]),.wb_dat_o(s3_dat_miso[7:0]), - .wb_we_i(s3_we),.wb_stb_i(s3_stb),.wb_cyc_i(s3_cyc), - .wb_ack_o(s3_ack),.wb_inta_o(), - .scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_pad_oen_o), - .sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_pad_oen_o) ); - - assign s3_dat_miso[15:8] = 8'd0; - - // I2C -- Don't use external transistors for open drain, the FPGA implements this - IOBUF scl_pin(.O(scl_pad_i), .IO(db_scl), .I(scl_pad_o), .T(scl_pad_oen_o)); - IOBUF sda_pin(.O(sda_pad_i), .IO(db_sda), .I(sda_pad_o), .T(sda_pad_oen_o)); - - // ///////////////////////////////////////////////////////////////////////// - // GPIOs - - wire [31:0] gpio_readback; - - gpio_atr #(.BASE(SR_GPIO), .WIDTH(32)) - gpio_atr(.clk(wb_clk),.reset(wb_rst), - .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), - .rx(run_rx0 | run_rx1), .tx(run_tx), - .gpio({io_tx,io_rx}), .gpio_readback(gpio_readback) ); - - //////////////////////////////////////////////////////////////////////////// - // FIFO to WB slave for async messages - Slave #5 - - //signals between fifo and buffer module - wire [35:0] _tx_err_data; - wire _tx_err_src_rdy, _tx_err_dst_rdy; - - fifo_cascade #(.WIDTH(36), .SIZE(9/*512 lines plenty for short pkts*/)) err_fifo( - .clk(wb_clk), .reset(wb_rst), .clear(wb_rst), - .datain(tx_err_data), .src_rdy_i(tx_err_src_rdy), .dst_rdy_o(tx_err_dst_rdy), - .dataout(_tx_err_data), .src_rdy_o(_tx_err_src_rdy), .dst_rdy_i(_tx_err_dst_rdy) - ); - - wire [31:0] err_status, err_data32; - //the buffer is 32 bits, but the data is 16, so mux based on the addr bit - assign s5_dat_miso = (s5_adr[1] == 1'b0)? err_data32[15:0] : err_data32[31:16]; - - buffer_int2 #(.BASE(SR_ERR_CTRL), .BUF_SIZE(5)) fifo_to_wb( - .clk(wb_clk), .rst(wb_rst), - .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), - .status(err_status), - // Wishbone interface to RAM - .wb_clk_i(wb_clk), .wb_rst_i(wb_rst), - .wb_we_i(s5_we), .wb_stb_i(s5_stb), - .wb_adr_i({5'b0,s5_adr}), .wb_dat_i({16'b0, s5_dat_mosi}), - .wb_dat_o(err_data32), .wb_ack_o(s5_ack), - // Write FIFO Interface - .wr_data_i(_tx_err_data), .wr_ready_i(_tx_err_src_rdy), .wr_ready_o(_tx_err_dst_rdy), - // Read FIFO Interface - .rd_data_o(), .rd_ready_o(), .rd_ready_i(1'b0) - ); - - //////////////////////////////////////////////////////////////////////////// - // Interrupts - - always @(posedge wb_clk) - proc_int <= (|err_status[1:0]); - - // ///////////////////////////////////////////////////////////////////////// - // Settings Bus -- Slave #8 + 9 - - // only have 64 regs, 32 bits each with current setup... - settings_bus_16LE #(.AWIDTH(11),.RWIDTH(8)) 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) ); - - user_settings #(.BASE(SR_USER_REGS)) user_settings - (.clk(wb_clk),.rst(wb_rst),.set_stb(set_stb), - .set_addr(set_addr),.set_data(set_data), - .set_addr_user(set_addr_user),.set_data_user(set_data_user), - .set_stb_user(set_stb_user) ); - - // ///////////////////////////////////////////////////////////////////////// - // Readback mux 32 -- Slave #7 - - //compatibility number -> increment when the fpga has been sufficiently altered - localparam compat_num = {16'd9, 16'd3}; //major, minor - - wire [31:0] reg_test32; - - //this setting reg is persistent across resets, to check for fpga loaded - setting_reg #(.my_addr(SR_REG_TEST32)) sr_reg_test32 - (.clk(wb_clk),.rst(/*wb_rst*/1'b0),.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({5'b0,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(reg_test32), .word05(err_status), - .word06(compat_num), .word07(gpio_readback), - .word08(32'b0), .word09(32'b0), - .word10(32'b0), .word11(32'b0), - .word12(32'b0), .word13(32'b0), - .word14(32'b0), .word15(32'b0) - ); - - // ///////////////////////////////////////////////////////////////////////// - // VITA Timing - - time_64bit #(.BASE(SR_TIME64)) time_64bit - (.clk(wb_clk), .rst(wb_rst), .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), - .pps(pps_in), .vita_time(vita_time), .vita_time_pps(vita_time_pps), .pps_int(pps_int), - .exp_time_in(0)); - - // ///////////////////////////////////////////////////////////////////////////////////// - // Debug circuitry - - assign debug_clk = 2'b00; //{ EM_CLK, clk_fpga }; - assign debug = 0; - -endmodule // u1e_core diff --git a/fpga/usrp2/top/N2x0/bootloader.rmi b/fpga/usrp2/top/N2x0/bootloader.rmi index 1b378b5d6..cf51f52b4 100644 --- a/fpga/usrp2/top/N2x0/bootloader.rmi +++ b/fpga/usrp2/top/N2x0/bootloader.rmi @@ -1,5 +1,5 @@ -defparam bootram.RAM0.INIT_00=256'h00000000_00000000_00000000_d7fc0400_3a0b0b80_80e4b40c_82700b0b_0b0b0b0b; -defparam bootram.RAM0.INIT_01=256'h00000000_00000000_00000000_800c0400_880c840c_80d8c62d_88080b0b_80088408; +defparam bootram.RAM0.INIT_00=256'h00000000_00000000_00000000_dbaa0400_3a0b0b80_80e7e80c_82700b0b_0b0b0b0b; +defparam bootram.RAM0.INIT_01=256'h00000000_00000000_00000000_800c0400_880c840c_80dbf42d_88080b0b_80088408; defparam bootram.RAM0.INIT_02=256'h00000000_00000000_04000000_ffff0652_832b2a83_81058205_72830609_71fd0608; defparam bootram.RAM0.INIT_03=256'h83a70400_0b0b0b0b_7383ffff_2b2b0906_05820583_83060981_83ffff73_71fd0608; defparam bootram.RAM0.INIT_04=256'h00000000_00000000_53510400_070a8106_73097306_09060906_72057373_72098105; @@ -18,404 +18,404 @@ defparam bootram.RAM0.INIT_10=256'h00000000_00000000_00000000_00000000_00000000_ defparam bootram.RAM0.INIT_11=256'h00000000_00000000_00000000_00000000_00000000_04000000_05055351_72720981; defparam bootram.RAM0.INIT_12=256'h00000000_00000000_00000000_00000000_00000000_07535104_73730906_72097206; defparam bootram.RAM0.INIT_13=256'h00000000_00000000_04000000_81ff0652_1010102a_81058305_72830609_71fc0608; -defparam bootram.RAM0.INIT_14=256'h00000000_00000000_88aa0400_060b0b0b_10100508_a0738306_0b0b80e4_71fc0608; -defparam bootram.RAM0.INIT_15=256'h00000000_0c510400_0c840c80_80085688_9d2d5050_0b0b80cf_88087575_80088408; -defparam bootram.RAM0.INIT_16=256'h00000000_0c510400_0c840c80_80085688_cf2d5050_0b0b80d0_88087575_80088408; +defparam bootram.RAM0.INIT_14=256'h00000000_00000000_88aa0400_060b0b0b_10100508_d4738306_0b0b80e7_71fc0608; +defparam bootram.RAM0.INIT_15=256'h00000000_0c510400_0c840c80_80085688_cb2d5050_0b0b80d2_88087575_80088408; +defparam bootram.RAM0.INIT_16=256'h00000000_0c510400_0c840c80_80085688_fd2d5050_0b0b80d3_88087575_80088408; defparam bootram.RAM0.INIT_17=256'h04000000_07515151_05ff0506_73097274_70547106_8106ff05_0509060a_72097081; defparam bootram.RAM0.INIT_18=256'h51040000_06075151_7405ff05_06730972_05705471_098106ff_0509060a_72097081; defparam bootram.RAM0.INIT_19=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_05ff0504; -defparam bootram.RAM0.INIT_1A=256'h00000000_00000000_00000000_00000000_00000000_51040000_80e4b00c_810b0b0b; +defparam bootram.RAM0.INIT_1A=256'h00000000_00000000_00000000_00000000_00000000_51040000_80e7e40c_810b0b0b; defparam bootram.RAM0.INIT_1B=256'h00000000_00000000_00000000_00000000_00000000_00000000_04000000_71810552; defparam bootram.RAM0.INIT_1C=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; defparam bootram.RAM0.INIT_1D=256'h00000000_00000000_00000000_00000000_00000000_04000000_10100552_02840572; defparam bootram.RAM0.INIT_1E=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; defparam bootram.RAM0.INIT_1F=256'h00000000_00000000_00000000_00000000_00000000_020d0400_05715351_717105ff; -defparam bootram.RAM0.INIT_20=256'h10101010_10101010_10101010_10101010_10101010_10101010_d0c33f04_82813f80; +defparam bootram.RAM0.INIT_20=256'h10101010_10101010_10101010_10101010_10101010_10101010_d3f13f04_82813f80; defparam bootram.RAM0.INIT_21=256'hfc060c51_102b0772_83051010_06098105_ff067383_51047381_10101053_10101010; defparam bootram.RAM0.INIT_22=256'h51535104_72ed3851_0a100a53_71105272_09720605_8106ff05_72728072_51043c04; -defparam bootram.RAM0.INIT_23=256'h800b80e5_8c0c82a0_0b0b80e5_8380800b_822ebd38_80e4b408_802ea438_80e4b008; -defparam bootram.RAM0.INIT_24=256'h0b80e590_80808280_e58c0cf8_0b0b0b80_808080a4_940c04f8_800b80e5_900c8290; -defparam bootram.RAM0.INIT_25=256'h940b80e5_80c0a880_80e58c0c_8c0b0b0b_80c0a880_e5940c04_84800b80_0cf88080; -defparam bootram.RAM0.INIT_26=256'h70085252_80e4bc08_5170a738_80e59833_04ff3d0d_80e5940c_80d8f80b_900c0b0b; -defparam bootram.RAM0.INIT_27=256'h9834833d_810b80e5_5270ee38_08700852_2d80e4bc_e4bc0c70_38841280_70802e94; -defparam bootram.RAM0.INIT_28=256'h38823d0d_09810685_800b802e_0b0b0b0b_802e8e38_80e58808_3d0d0b0b_0d040480; -defparam bootram.RAM0.INIT_29=256'h3d225a79_80d33895_0d685b7a_0404ee3d_3f823d0d_0b0bf5d4_e588510b_040b0b80; -defparam bootram.RAM0.INIT_2A=256'h2e973881_79838086_3881e439_80842e8e_8b387983_83808524_80c23879_8380852e; -defparam bootram.RAM0.INIT_2B=256'h7a81e2c4_81e2c00c_39890a0b_e1880c99_840c7a81_0c7a81e1_0b81e180_da39890a; -defparam bootram.RAM0.INIT_2C=256'h2e9e3879_79838085_973d225a_7c2eab38_0c805c7a_7a81e4d0_c80c8639_0c7a81e2; -defparam bootram.RAM0.INIT_2D=256'h815c923d_38818039_80862e86_825c7983_38818c39_80842e92_8b387983_83808524; -defparam bootram.RAM0.INIT_2E=256'h3d415e5c_0b883d99_5b5f4080_0284057d_943f8008_a9893f8a_84055241_7053963d; -defparam bootram.RAM0.INIT_2F=256'h7d055b5b_7b1d963d_901f5e5c_ef38800b_5c887c26_7b34811c_5b5b7933_7b1d7f1d; -defparam bootram.RAM0.INIT_30=256'h811c5c86_79337b34_601d5b5b_5e5c7b1d_800b881f_7c26ed38_811c5c88_79337b34; -defparam bootram.RAM0.INIT_31=256'h3d0d04ee_99ff3f94_7c26ef38_811c5c86_79337b34_611d5b5b_805c7b1e_7c26ef38; -defparam bootram.RAM0.INIT_32=256'h75538b52_802e8c38_2e943875_0856758b_279c3877_5a588379_84120859_3d0d686a; -defparam bootram.RAM0.INIT_33=256'h8b5ba05c_f93fa057_d9cc5194_53a45280_268e3878_e15778a3_958c3f80_80d8fc51; -defparam bootram.RAM0.INIT_34=256'h89a23f80_0480c15c_05567508_2980dbbc_91387584_75922682_ff9f1756_8818085d; -defparam bootram.RAM0.INIT_35=256'h962a8480_32703070_183380f2_2e923894_577580f2_33568880_fb399518_08085e81; -defparam bootram.RAM0.INIT_36=256'h83388157_5775772e_97193357_0852800b_08538c18_33549018_76559618_06595156; -defparam bootram.RAM0.INIT_37=256'h568cd23f_8c193352_3dea0553_33705495_b3398d18_80d35c81_3f80085f_765196a2; -defparam bootram.RAM0.INIT_38=256'hb5053480_75028405_3f80c85c_52568ddf_538c1933_70548e19_398d1833_80c95c94; -defparam bootram.RAM0.INIT_39=256'h0476085f_58567508_058c1908_2980dc88_c2387584_75852680_33ff0556_ff399418; -defparam bootram.RAM0.INIT_3A=256'h70084056_80e59c05_39768429_2277239b_a2399218_1808770c_5fa93990_ae397622; -defparam bootram.RAM0.INIT_3B=256'h55943ddc_5c8c1808_785e80cc_d25cad39_710c5680_05901908_2980e59c_8e397684; -defparam bootram.RAM0.INIT_3C=256'ha439a05c_7826ed38_81185888_75337734_79055757_7719963d_833d5a58_0554800b; -defparam bootram.RAM0.INIT_3D=256'h887826ed_34811858_57753377_3d790557_58771996_0b833d5a_dc055480_a455943d; -defparam bootram.RAM0.INIT_3E=256'h2e9238a0_993f7280_98525392_705380da_fe3d0d74_943d0d04_519b8b3f_38838080; -defparam bootram.RAM0.INIT_3F=256'h843d0d04_518fba3f_87c63f72_a0527251_9e3f8d39_8fcb3f9b_d73f8151_52a05187; -defparam bootram.RAM1.INIT_00=256'h91d43f81_80daf451_dd3f8b52_dad45191_3f885280_b85188ba_8d3f80da_fa3d0d82; -defparam bootram.RAM1.INIT_01=256'h3f800851_d43f8684_908f3f87_3f800851_843f85b1_859d3f86_0ca9a73f_0b80e5b8; -defparam bootram.RAM1.INIT_02=256'h85528008_e73f8380_94b63f85_52800851_85933f73_3f800854_c83f85f8_90a93f87; -defparam bootram.RAM1.INIT_03=256'hb2528380_94f23f8a_83808451_3f8ab252_805194fc_bf528380_94c03f8c_518e9a3f; -defparam bootram.RAM1.INIT_04=256'h9251a584_d33f8380_80825194_c0865283_94de3f80_83808551_3f8ab252_865194e8; -defparam bootram.RAM1.INIT_05=256'h802e80c9_08568008_8e873f80_3dfc0551_abe93f88_51a9d73f_903f8fcc_3f80518e; -defparam bootram.RAM1.INIT_06=256'h055180c4_52800890_5380db98_06ad3884_ee2e0981_557382fd_8e052255_38768008; -defparam bootram.RAM1.INIT_07=256'h3f883974_73518591_3f86963f_52548ef7_3f941670_a0519086_9a3880db_d43f8008; -defparam bootram.RAM1.INIT_08=256'hb73f8787_91ce3f82_0d85df3f_9e39fe3d_8bfe3fff_3fa4d73f_8b3f8d8c_5275519a; -defparam bootram.RAM1.INIT_09=256'h52845184_8af23f84_3f82ac51_80518588_ac3f9f52_52805185_88833f9f_3f8bb33f; -defparam bootram.RAM1.INIT_0A=256'h82ac518a_5184e13f_3f905290_ac518ad8_84ee3f82_88528851_518ae53f_fb3f82ac; -defparam bootram.RAM1.INIT_0B=256'haf3fff13_80e4518a_5184c53f_3f9f529c_e4518abc_84d23f80_9f528051_cb3f8253; -defparam bootram.RAM1.INIT_0C=256'h800c843d_840c810b_890b81e0_5184a93f_3f9f5281_9e5184cd_df389f52_53728025; -defparam bootram.RAM1.INIT_0D=256'h8c05a705_7a7d7f02_04f93d0d_51823d0d_8106800c_08708b2a_0d82808c_0d04803d; -defparam bootram.RAM1.INIT_0E=256'h88388855_5575832e_80258805_2e933872_59577582_5a575758_80258205_33703070; -defparam bootram.RAM1.INIT_0F=256'h2cff0577_97387681_5472802e_259e3872_80548177_9f2a5153_55733070_7383388a; -defparam bootram.RAM1.INIT_10=256'h7281ff06_51aea93f_5474527b_73538180_80548639_07515454_7072842b_7131fe05; -defparam bootram.RAM1.INIT_11=256'h0d029f05_0d04fb3d_8f3f893d_80da51ae_973f8152_811851ae_9f3f7352_527751ae; -defparam bootram.RAM1.INIT_12=256'hade63f81_5280c551_fe3d0d81_873d0d04_51fee63f_53785275_80ca54bd_33568155; -defparam bootram.RAM1.INIT_13=256'h0781e080_80087090_f33881e0_5372802e_0881ff06_feb83f80_3f8f883f_528151d6; -defparam bootram.RAM1.INIT_14=256'h52527080_72177033_76279e38_54805372_57817056_0d787a57_0d04fa3d_0c53843d; -defparam bootram.RAM1.INIT_15=256'h83388151_5170802e_39747407_811353df_83388055_7181ff2e_54713352_2e833880; -defparam bootram.RAM1.INIT_16=256'h3f843d0d_c451beb1_a45280e4_865380dc_80e5c034_3d0d810b_3d0d04fe_70800c88; -defparam bootram.RAM1.INIT_17=256'hd051ade5_56825280_873d7054_c0348654_810b80e5_5574bc38_80e5c033_04f93d0d; -defparam bootram.RAM1.INIT_18=256'h86537552_55748c38_0881ff06_fef43f80_86527551_802e9c38_ff065574_3f800881; -defparam bootram.RAM1.INIT_19=256'he4c00c04_dca00880_e5bc3480_04810b80_0c893d0d_e4c40b80_bde73f80_80e4c451; -defparam bootram.RAM1.INIT_1A=256'h51ad863f_8c5280d0_3dfc0553_34845487_0b80e5bc_74b93881_e5bc3355_fb3d0d80; -defparam bootram.RAM1.INIT_1B=256'h86387580_ff065574_3f800881_0551fe92_52873dfc_2e993884_06557480_800881ff; -defparam bootram.RAM1.INIT_1C=256'hcc3f8008_80d051ab_75538c52_77568454_04fb3d0d_0c873d0d_e4c00b80_e4c00c80; -defparam bootram.RAM1.INIT_1D=256'h803d0d73_873d0d04_3474800c_0b80e5bc_e4c00c81_38750880_74802e8d_81ff0655; -defparam bootram.RAM1.INIT_1E=256'h73097375_04803d0d_51823d0d_81e08c0c_80e5c40c_08060770_7180e5c4_09737506; -defparam bootram.RAM1.INIT_1F=256'h0d747053_3f04fe3d_0d0481af_0c51823d_0c81e098_7080e5c8_c8080607_067180e5; -defparam bootram.RAM1.INIT_20=256'h3d0d7779_3d0d04fb_81b63f83_8a528051_04ff3d0d_0c843d0d_c73f7280_53805181; -defparam bootram.RAM1.INIT_21=256'he539800b_5581913f_06537652_157481ff_2e903881_54547280_7081ff06_56567433; -defparam bootram.RAM1.INIT_22=256'hffbd3f8a_53705253_3d0d7476_3d0d04fe_51cd3f83_0d735280_0d04ff3d_800c873d; -defparam bootram.RAM1.INIT_23=256'h3d0d7251_3d0d0480_51dd3f83_0d735280_0d04ff3d_800c843d_e73f800b_52725180; -defparam bootram.RAM1.INIT_24=256'h05702272_1080dcac_90800575_73a02982_04ff3d0d_34823d0d_80e4cc12_028f0533; -defparam bootram.RAM1.INIT_25=256'h7251ce3f_d0133352_c63f80e4_33527251_80e4cc13_3d0d8053_3d0d04fe_0c535183; -defparam bootram.RAM1.INIT_26=256'he4cc1433_06953880_8a2e0981_78565474_fc3d0d76_843d0d04_7325e538_81135382; -defparam bootram.RAM1.INIT_27=256'h802ef838_14085372_80055484_a0298290_51de3f73_388d5273_09810687_5372812e; -defparam bootram.RAM1.INIT_28=256'h85389012_5370802e_085252ff_80058811_a0298290_fe3d0d74_863d0d04_748c150c; -defparam bootram.RAM1.INIT_29=256'h800c7088_ff0681a8_d8227081_880c80e4_800b81a8_04ff3d0d_0c843d0d_08537280; -defparam bootram.RAM1.INIT_2A=256'h55535481_05970533_76780288_04fd3d0d_0c833d0d_0b81a888_0c518180_2a81a884; -defparam bootram.RAM1.INIT_2B=256'ha88c0c81_10810781_70f13872_06515151_862a7081_a8900870_81863881_5171802e; -defparam bootram.RAM1.INIT_2C=256'h2a708106_90087087_f13881a8_51515170_2a708106_90087081_900c81a8_900b81a8; -defparam bootram.RAM1.INIT_2D=256'h81a8900c_38a05170_71812e83_3880e851_71802eb1_802eba38_51515170_70813251; -defparam bootram.RAM1.INIT_2E=256'hff1252cc_81055634_51707470_81a88c08_5170f138_81065151_70812a70_81a89008; -defparam bootram.RAM1.INIT_2F=256'h05335553_02880597_3d0d7678_3d0d04fd_70800c85_81a8900c_3980c00b_39815188; -defparam bootram.RAM1.INIT_30=256'h2e843881_d0517180_a88c0c81_38721081_515170f1_70810651_0870862a_5481a890; -defparam bootram.RAM1.INIT_31=256'h872a7081_a8900870_70f13881_06515151_812a7081_a8900870_a8900c81_90517081; -defparam bootram.RAM1.INIT_32=256'h5171812e_8c0c80d0_733381a8_2e80c538_cf387180_70802e80_51515151_06708132; -defparam bootram.RAM1.INIT_33=256'h0870872a_3881a890_515170f1_70810651_0870812a_0c81a890_7081a890_83389051; -defparam bootram.RAM1.INIT_34=256'h80c00b81_81518a39_54ffb739_14ff1353_2e8e3881_51517080_81325151_70810670; -defparam bootram.RAM1.INIT_35=256'h5281b8ac_81b8ac08_74259b38_54805372_fd3d0d75_853d0d04_5170800c_a8900c80; -defparam bootram.RAM1.INIT_36=256'h80880c81_0dff0b82_0d04ff3d_e239853d_38811353_9f7127f1_5151868d_08707331; -defparam bootram.RAM1.INIT_37=256'h8405540c_9efc7270_f0528751_8c0c80ef_ff0b8280_8280840c_800cef0b_e20b8280; -defparam bootram.RAM1.INIT_38=256'h51528053_08710658_0982808c_80880870_fb3d0d82_833d0d04_8025f138_ff115170; -defparam bootram.RAM1.INIT_39=256'h8f398113_82808c0c_52712d74_72517308_802e8f38_76065271_f0555574_810b80ef; -defparam bootram.RAM1.INIT_3A=256'h2980eff0_9f387184_52718726_ff3d0d73_873d0d04_7325dc38_57555387_84157610; -defparam bootram.RAM1.INIT_3B=256'h04ff3d0d_833d0d04_0c535152_06828088_88087072_70098280_5181722b_0575710c; -defparam bootram.RAM1.INIT_3C=256'h0c81b8a0_0b81e0cc_803d0d81_833d0d04_81e0c80c_e0c40c52_74700881_02920522; -defparam bootram.RAM1.INIT_3D=256'h04fe3d0d_81e0c00c_04de3f71_0c823d0d_0b81e0cc_2ef33882_51517080_08708406; -defparam bootram.RAM1.INIT_3E=256'h80529a39_53538180_902a710c_a0087571_933881b8_5272802e_70810654_81b8a008; -defparam bootram.RAM1.INIT_3F=256'h843d0d04_5271800c_ff9e3f72_51f8d33f_3880dcb8_71802e8b_81065152_71812a70; -defparam bootram.RAM2.INIT_00=256'hff3d0d02_823d0d04_800b800c_f2388180_5170802e_80c00651_b8a00870_803d0d81; -defparam bootram.RAM2.INIT_01=256'h0b81e0cc_2ef33884_51517080_08709006_5281b8a0_81e0cc0c_902b8807_8e052270; -defparam bootram.RAM2.INIT_02=256'ha5c63f81_70335252_a53f7214_38ba51f7_72802e86_75548053_04fd3d0d_0c833d0d; -defparam bootram.RAM2.INIT_03=256'h33535680_11335470_11335581_11335682_3d0d7783_3d0d04fb_27e63885_13538573; -defparam bootram.RAM2.INIT_04=256'h515b5f5d_30709f2a_bb053370_63029005_0d7c7e61_0d04f63d_ed3f873d_dcbc5180; -defparam bootram.RAM2.INIT_05=256'h55785480_26943879_30577777_51782d76_387952ad_75802e8a_80258f38_5b595776; -defparam bootram.RAM2.INIT_06=256'h51782d8c_dcc80533_3f800880_7651ada4_bd3f7752_800851ff_51ad8c3f_53775276; -defparam bootram.RAM2.INIT_07=256'h08a1e35c_70840552_0d8c3d70_0d04f73d_8d3f823d_053351f6_3d0d028b_3d0d0480; -defparam bootram.RAM2.INIT_08=256'hdb388119_09810680_5675a52e_7681ff06_2e81d138_57577580_7081ff06_5a587833; -defparam bootram.RAM2.INIT_09=256'h3875802e_80e3248a_2eb93875_387580e3_80f024a0_80fb3875_7580f02e_70335759; -defparam bootram.RAM2.INIT_0A=256'h7580f32e_f5248b38_ac387580_7580f52e_38818b39_e42e80c6_95397580_819e3881; -defparam bootram.RAM2.INIT_0B=256'h792d80da_80527551_33525956_84198312_80ec3977_f82eba38_f5397580_80db3880; -defparam bootram.RAM2.INIT_0C=256'ha1e35481_59568055_19710852_90397784_e3548053_568055a1_71085259_39778419; -defparam bootram.RAM2.INIT_0D=256'h39778419_fdd03f9e_90527551_e3548053_568055a1_71085259_39778419_538a5292; -defparam bootram.RAM2.INIT_0E=256'h59fea339_ec398119_3351792d_70810558_38805276_75802e8e_56763356_71085959; -defparam bootram.RAM2.INIT_0F=256'h51515170_2a708106_90087088_3d0d81a0_940c0480_810b81a0_8b3d0d04_800b800c; -defparam bootram.RAM2.INIT_10=256'h5354d03f_c0800755_06077080_067b8c80_337980ff_0d029705_0d04fd3d_f138823d; -defparam bootram.RAM2.INIT_11=256'h73517380_81a0900c_80c28007_a0900c71_800c7281_0c7781a0_0681a098_7683ffff; -defparam bootram.RAM2.INIT_12=256'h53727427_54555580_0d76787a_0d04fc3d_800c853d_80085170_aa3f81a0_2e8938ff; -defparam bootram.RAM2.INIT_13=256'hff067290_387183ff_70802e8d_71902a51_5351ee39_05811555_15702273_8f387210; -defparam bootram.RAM2.INIT_14=256'h0880e5d8_f43f7670_e5d051ae_53755280_fd3d0d86_863d0d04_3971800c_2a0552ec; -defparam bootram.RAM2.INIT_15=256'h38833d0d_708025f3_ff125252_720c8812_52895180_0d80e5e0_0d04ff3d_0c54853d; -defparam bootram.RAM2.INIT_16=256'h52528972_81128812_742e8e38_70225472_e5dc5252_53800b80_02960522_04fd3d0d; -defparam bootram.RAM2.INIT_17=256'h08802e89_56c73f80_ff065358_7a7183ff_fa3d0d78_853d0d04_5170800c_25ee3880; -defparam bootram.RAM2.INIT_18=256'h802e8f38_15555271_55730888_e5dc5555_e5e00b80_39800880_84050cad_38768008; -defparam bootram.RAM2.INIT_19=256'h86705493_04f13d0d_0c883d0d_23768414_883f7573_25eb389c_54558975_81158814; -defparam bootram.RAM2.INIT_1A=256'h028405a2_b43f9080_dc0551ad_0552913d_53923d88_adc33f73_d6055254_3d53923d; -defparam bootram.RAM2.INIT_1B=256'h052380c0_028405aa_23818080_800b8c3d_05a60523_23800284_800b8b3d_0523818a; -defparam bootram.RAM2.INIT_1C=256'h80080284_51fdb73f_913de405_80538a52_685d665e_05ae0523_23800284_910b8d3d; -defparam bootram.RAM2.INIT_1D=256'hbe0523ac_80028405_0b913d23_ba052380_22028405_3d23963d_983d2290_05ae0523; -defparam bootram.RAM2.INIT_1E=256'h0b973d23_0d805b80_0d04e83d_8c3f913d_8405519e_c02981e6_05526980_53913dd4; -defparam bootram.RAM2.INIT_1F=256'h80f20522_ac933f02_3df80551_e5d0529a_3f865380_0551aca1_529a3df2_86539b3d; -defparam bootram.RAM2.INIT_20=256'ha13d0845_05436e44_c41143f0_800b9b3d_8008585a_f73f8008_e20523f7_02840580; -defparam bootram.RAM2.INIT_21=256'h7508701a_3d568458_fc06408c_3d088305_3d085fa3_5d6e5ea1_59845c90_a33d0846; -defparam bootram.RAM2.INIT_22=256'h83065473_2e9a3873_08547380_73760c75_75278438_565a5573_80713151_787c3190; -defparam bootram.RAM2.INIT_23=256'h519cde3f_16085276_75085394_51effb3f_3880dce4_73802e88_08830654_8c389416; -defparam bootram.RAM2.INIT_24=256'h51f6fd3f_5978822a_843880c0_3878bf26_8025ffac_19595777_570817ff_75708405; -defparam bootram.RAM2.INIT_25=256'hca052380_02840580_94055a79_3d237f1f_8a800b94_6e404081_ea3d0d6b_9a3d0d04; -defparam bootram.RAM2.INIT_26=256'h80d20523_80028405_79963d23_c080075a_05236980_840580ce_81808002_0b953d23; -defparam bootram.RAM2.INIT_27=256'hd2052391_02840580_08095a79_fae03f80_3d70525c_538a5293_46684780_80e5d808; -defparam bootram.RAM2.INIT_28=256'h7a51f6cb_51f7d73f_3880dd90_065a7992_800881ff_5e8ac83f_3d70535c_3d705398; -defparam bootram.RAM2.INIT_29=256'h1f5b5b79_5c7b1d7c_90805380_94557b54_586b575d_5a6d5960_a939027f_3fedea3f; -defparam bootram.RAM2.INIT_2A=256'h3d238d3d_ae05228a_0d7f5802_0d04f73d_893f983d_26ef38fd_1c5c867c_337b3481; -defparam bootram.RAM2.INIT_2B=256'h3df80553_5588548b_2377567e_8405a605_3d238002_1857768b_a2052388_22028405; -defparam bootram.RAM2.INIT_2C=256'h0b8f3d34_b2052386_80028405_8e3d2390_3d0d810b_3d0d04ee_fe9e3f8b_91527d51; -defparam bootram.RAM2.INIT_2D=256'hd03feb80_ec0551a8_0852943d_3f865380_0523eab1_028405b6_b5053481_84028405; -defparam bootram.RAM2.INIT_2E=256'h3f800808_cd3feae4_f60551a9_8052943d_c03f8653_f20551a8_0852943d_3f845380; -defparam bootram.RAM2.INIT_2F=256'hdc1b337a_1c5a80dc_53805b7a_05549086_55943de4_5780569c_59805880_43025c80; -defparam bootram.RAM2.INIT_30=256'h90862e09_225f5d7d_3d088e11_d93d0daa_943d0d04_38fbcb3f_867b26ef_34811b5b; -defparam bootram.RAM2.INIT_31=256'hb53f86ee_ddc051f5_38795280_799b268d_f2055b5b_3d088429_38901dac_8106829d; -defparam bootram.RAM2.INIT_32=256'h1b225a79_86d43884_2e098106_5a799080_38821b22_810686e2_79812e09_397a225a; -defparam bootram.RAM2.INIT_33=256'h853fa81d_70524088_b9389e1d_09810686_5a79812e_38861b22_810686c6_8c842e09; -defparam bootram.RAM2.INIT_34=256'h08868f38_80085c80_51a6823f_3dffa805_e5d852a9_43845380_fd3f8008_70525f87; -defparam bootram.RAM2.INIT_35=256'h23841b33_0580fe05_1b220284_a13d2382_e03f7a22_527951a6_5380e5d0_a73d5a86; -defparam bootram.RAM2.INIT_36=256'h0551a6ad_52a93de4_23865379_05818205_34820284_05818105_1b330284_a23d3485; -defparam bootram.RAM2.INIT_37=256'h903f7953_527a51a6_8653981d_818e055b_a69f3f02_ea05525a_7f53aa3d_3f847054; -defparam bootram.RAM2.INIT_38=256'h587c575d_5a7c597c_f83f027c_527e51a5_5f86537a_843f9e3d_f40551a6_7f52a93d; -defparam bootram.RAM2.INIT_39=256'h993f84ee_26ef38f9_1c5c867c_337b3481_1d5b5b79_537b1d7f_dc05547d_9c55a93d; -defparam bootram.RAM2.INIT_3A=256'hd1387988_09810684_5b60842e_8c2a435b_1d702270_84e43890_2e098106_397d9080; -defparam bootram.RAM2.INIT_3B=256'h5e865380_84b4387e_ff065f7e_1b2280ff_84c03886_2e098106_515a7985_2a708f06; -defparam bootram.RAM2.INIT_3C=256'ha3fb3f80_70535b5c_80e5d854_901c6255_38815e7e_3f800883_1d51a491_dcdc5282; -defparam bootram.RAM2.INIT_3D=256'h22ec1140_1b33821c_84b83f89_529c1d51_8138881d_7b802e84_5c7d8738_08833881; -defparam bootram.RAM2.INIT_3E=256'h5d42407d_8411225d_7a08a41f_388c1b08_810683de_7f912e09_2e81bb38_5d407f81; -defparam bootram.RAM2.INIT_3F=256'hf5c33f80_22535d5d_e41d821d_bd39ac1d_f2843f83_80dde051_79537d52_7a2e8f38; -defparam bootram.RAM3.INIT_00=256'ha3ef3f9c_7d527951_5f5a8853_9a3d993d_3d237f49_387a2299_802e83a6_08428008; -defparam bootram.RAM3.INIT_01=256'h51a3ce3f_b4055279_53a93dff_23604788_1b22973d_a3e33f82_79527f51_3d408853; -defparam bootram.RAM3.INIT_02=256'h811c5c88_79337b34_7c1f5b5b_5e5c7b1d_557e843d_3f7b567c_7d51a3c5_88537952; -defparam bootram.RAM3.INIT_03=256'h5a792d82_61840508_7b26ef38_811b5b88_84051c34_5a793302_805b7f1b_7c26ef38; -defparam bootram.RAM3.INIT_04=256'h335a7983_9539811a_81bb3882_387d882e_7d832e8a_33405b42_08a41e70_ad398c1b; -defparam bootram.RAM3.INIT_05=256'h2251f481_81f4387c_2e098106_5e5c7991_8912335c_1d80c01e_81a238ac_2e098106; -defparam bootram.RAM3.INIT_06=256'h88537a52_9b3d5c5e_794b983d_229b3d23_1c085a7c_80fe388c_8008802e_3f800841; -defparam bootram.RAM3.INIT_07=256'h4d8853a9_9d3d2379_5a821d22_3f901c08_7f51a29d_88537d52_3f963d40_7d51a2a9; -defparam bootram.RAM3.INIT_08=256'h1d7c1f5b_3d5e5c7b_7e557e84_fc3f7e56_527d51a1_3f88537a_7a51a285_3dcc0552; -defparam bootram.RAM3.INIT_09=256'h887b26ef_34811b5b_0284051c_1b5a7933_38805b7f_887c26ef_34811c5c_5b79337b; -defparam bootram.RAM3.INIT_0A=256'h02840580_953d347e_1d5d5d7e_39ac1de4_ad3f80de_80e951e5_085a792d_38608405; -defparam bootram.RAM3.INIT_0B=256'h53605294_d205237e_02840580_23861a22_1a22963d_ce052384_02840580_cd05347e; -defparam bootram.RAM3.INIT_0C=256'hce05237b_02840580_08095a79_f1c03f80_2a527c51_08537b81_f1cc3f80_3d70525b; -defparam bootram.RAM3.INIT_0D=256'h53727427_e6ac0855_0d800b80_0d04fc3d_f73fa93d_526151f5_547a537f_567c557d; -defparam bootram.RAM3.INIT_0E=256'h39811353_3872518b_09810685_5170752e_088c1353_54565171_0880e6b4_a4387670; -defparam bootram.RAM3.INIT_0F=256'h8025ba38_b93f8008_535755ff_0d777971_0d04fb3d_800c863d_38ff5170_737326e7; -defparam bootram.RAM3.INIT_10=256'hb00c5473_870680e6_b0088111_8e3980e6_80e6ac0c_89388114_54738726_80e6ac08; -defparam bootram.RAM3.INIT_11=256'h80080554_39800810_b8145194_755280e6_51548653_e6b4120c_2b760880_10147082; -defparam bootram.RAM3.INIT_12=256'h54738008_fed83f80_3d0d7551_3d0d04fd_9fbf3f87_e6b80551_73842980_86537552; -defparam bootram.RAM3.INIT_13=256'h800c853d_3f815473_76519f95_e6b80552_73842980_05548653_08108008_24993880; -defparam bootram.RAM3.INIT_14=256'h33710780_72078316_3370882b_2b078214_982b7190_81123371_0d757033_0d04fd3d; -defparam bootram.RAM3.INIT_15=256'hffff068b_a8387383_56595776_80e79422_3d0d7d7f_3d0d04f9_56545285_0c525354; -defparam bootram.RAM3.INIT_16=256'h742380c0_05515476_2980e798_29147090_d3387390_73832680_31525654_3d227072; -defparam bootram.RAM3.INIT_17=256'h3d527390_5488538a_74902915_8326ad38_57575474_22707231_ff068d3d_397383ff; -defparam bootram.RAM3.INIT_18=256'h1656ec39_e3b23f81_53547451_75177033_78279138_3f805675_05519e85_2980e798; -defparam bootram.RAM3.INIT_19=256'h88140c80_23800b82_54548073_0b80e798_e7942380_9a052280_fc3d0d02_893d0d04; -defparam bootram.RAM3.INIT_1A=256'hd938863d_54837427_82901454_9b3f8114_740551ef_80e79422_0cb5ab52_0b828c14; -defparam bootram.RAM3.INIT_1B=256'h881a085b_be387582_51567581_32708106_847c2c81_e7985a5c_0d800b80_0d04f43d; -defparam bootram.RAM3.INIT_1C=256'hff06708a_38800881_ff2e80c5_f73f8008_5b7b51e2_781a8805_2680d638_5d7981ff; -defparam bootram.RAM3.INIT_1D=256'h777b7081_8338815d_5876802e_51595158_80250753_72802571_8d327030_32703072; -defparam bootram.RAM3.INIT_1E=256'h38828819_7a27ffb1_1a5a81ff_8c1a0c81_0c800b82_0582881a_88190881_055d3482; -defparam bootram.RAM3.INIT_1F=256'h75802eab_38782256_8b7627bf_8c1b0c56_08811182_38828c19_d2387c91_08802e80; -defparam bootram.RAM3.INIT_20=256'h887826ef_34811858_57753377_1a781a57_3d5b5877_54800b83_08558819_38828819; -defparam bootram.RAM3.INIT_21=256'h5a5c837c_1c82901a_8c1a0c81_0c800b82_0b82881a_f2a83f80_227c0551_3880e794; -defparam bootram.RAM3.INIT_22=256'h9d055755_80028405_5194d53f_80c05268_3d705457_ea3d0d88_8e3d0d04_27fea938; -defparam bootram.RAM3.INIT_23=256'h81992e09_33515473_38741670_09810694_7381aa2e_ff2e9d38_51547381_74177033; -defparam bootram.RAM3.INIT_24=256'h863d7054_04f93d0d_0c983d0d_80547380_7527d138_811555be_81548b39_81068538; -defparam bootram.RAM3.INIT_25=256'h83388155_2e098106_3f800875_735199e5_80de8452_80558453_5194853f_54845279; -defparam bootram.RAM3.INIT_26=256'h55805189_0881ff06_8add3f80_0d8df23f_0c04fc3d_0b81e094_3d0d0481_74800c89; -defparam bootram.RAM3.INIT_27=256'h80dec051_3974b538_88518183_883880de_51515473_2a708106_b408708d_dc3f81b8; -defparam bootram.RAM3.INIT_28=256'h82ac51e3_5189a23f_ded83f81_80deec51_802e9a38_bf3f8008_800a51fe_deec3fb0; -defparam bootram.RAM3.INIT_29=256'h3880dff0_08802ebb_fee33f80_98800a51_5180cc39_3f80dfa4_0a5184b5_8b3fb080; -defparam bootram.RAM3.INIT_2A=256'h51e2cd3f_953f82ac_e09c51de_92da3f80_98800a51_80ffff52_83808053_51deab3f; -defparam bootram.RAM3.INIT_2B=256'hf13f863d_e0fc51dd_3f883980_805183e9_51e2bd3f_853f82ac_e0c051de_fee53f80; -defparam bootram.RAM3.INIT_2C=256'h3f80efd8_a051dca0_dd3fa052_c85254e6_705380e1_fd3d0d75_efd80c04_0d047180; -defparam bootram.RAM3.INIT_2D=256'h08537280_3f80efd8_8051dc84_3d0da052_3d0d04fe_51722d85_2e853873_08537280; -defparam bootram.RAM3.INIT_2E=256'h51535481_2a708106_0b800886_89a83fff_3d0d9a51_3d0d04fc_51722d84_2e853880; -defparam bootram.RAM3.INIT_2F=256'h248a388a_38718280_82802e9b_80e45471_80065355_0b800886_80ec3882_5571802e; -defparam bootram.RAM3.INIT_30=256'h5188db3f_80085285_5188e33f_38ff5484_84802e83_87e85471_8e388a39_5471802e; -defparam bootram.RAM3.INIT_31=256'h53515452_80e28055_80efe40c_c0113370_720780e2_2c708306_0680088a_71882a8c; -defparam bootram.RAM3.INIT_32=256'hefdc0c74_98387480_efdc082e_9d3f7480_085252dc_80e4e011_822b8c06_dc843f71; -defparam bootram.RAM3.INIT_33=256'he0082e8e_387380ef_09810696_3974822e_fec13f9e_8106a338_74812e09_822ea638; -defparam bootram.RAM3.INIT_34=256'h3f800851_3d0dd8c5_3d0d04fd_87e83f86_fb3f9951_3f7351fd_e00cfea7_387380ef; -defparam bootram.RAM3.INIT_35=256'hae80529c_87f13f81_8d529851_5187c73f_efe00c99_0cff0b80_0b80efdc_87bd3f80; -defparam bootram.RAM3.INIT_36=256'h845187cb_06705354_8007f49f_3f800890_845187aa_51e1853f_bbcb5284_5187e83f; -defparam bootram.RAM3.INIT_37=256'h3f800884_805186fe_51e3fb3f_5280e298_80085373_082e8d38_953f7380_3f845187; -defparam bootram.RAM3.INIT_38=256'h71832a84_71872a07_852a8206_97053370_fd3d0d02_853d0d04_5187a43f_80075280; -defparam bootram.RAM3.INIT_39=256'h852b80c0_81ff0676_73070770_2ba00671_90067483_07077310_88067173_0672812a; -defparam bootram.RAM3.INIT_3A=256'h04fe3d0d_52853d0d_55525555_51525351_82c0800c_7081ff06_78872b07_06707207; -defparam bootram.RAM3.INIT_3B=256'h9951ff8c_ff923f81_3f81aa51_ff51ff98_ff9e3f81_5381ff51_81d00a07_74d00a06; -defparam bootram.RAM3.INIT_3C=256'h51feed3f_7281ff06_52fef53f_81ff0652_72882a70_51ff813f_873f80e1_3fb251ff; -defparam bootram.RAM3.INIT_3D=256'hcf3fb051_065253fe_2a7081ff_db3f7290_982a51fe_fee23f72_3f818151_b251fee8; -defparam bootram.RAM3.INIT_3E=256'h3fa051fe_8051feb0_51feb53f_feba3fa0_bf3f8e51_3f8051fe_a151fec4_feca3f81; -defparam bootram.RAM3.INIT_3F=256'h3f863d22_d05183e9_53805280_873dfc05_3d0d8254_3d0d04fb_fea63f84_ab3f8051; -defparam bootram.RAM4.INIT_00=256'h90387753_77829326_08585957_3d088412_3d0880d7_3d0d80d5_0d04ffb2_800c873d; -defparam bootram.RAM4.INIT_01=256'h9c055675_842980e3_81cc3875_56759626_39ff9f16_c93f81d6_e2d051e1_82945280; -defparam bootram.RAM4.INIT_02=256'he1880c89_0c800b81_0b81e184_e1800c80_890a0b81_8008085e_5cd5f93f_080480c1; -defparam bootram.RAM4.INIT_03=256'h9a3f8008_818a398c_81e4d00c_c80c800b_800b81e2_81e2c40c_c00c800b_0a0b81e2; -defparam bootram.RAM4.INIT_04=256'h39901708_d65c80e8_ff065e80_800883ff_39fedc3f_c65c80f8_80085f80_5e8c9e3f; -defparam bootram.RAM4.INIT_05=256'hff065675_3f800881_90518abb_d33980f0_80c55c80_5189f63f_5280f090_538c1708; -defparam bootram.RAM4.INIT_06=256'h80d75ca4_5188dd3f_528c1708_53901708_b7399417_3980c25c_80c45cbc_802e8638; -defparam bootram.RAM4.INIT_07=256'h51fcde3f_80d35c80_d25c8d39_8bba3f80_8c170851_90170852_fe800553_3980d03d; -defparam bootram.RAM4.INIT_08=256'h57753377_3d790557_771980d2_833d5a58_0554800b_d03dfdec_82945580_8339a05c; -defparam bootram.RAM4.INIT_09=256'hd6883fff_80e3f851_04803d0d_80d03d0d_51e8a33f_38838082_887826ec_34811858; -defparam bootram.RAM4.INIT_0A=256'h75538152_80559854_07575788_3371882b_8405ab05_a7053302_f93d0d02_5183983f; -defparam bootram.RAM4.INIT_0B=256'hb7387581_54807425_74ff1656_5a575758_7a7c7f7f_04f83d0d_3f893d0d_8051e1a2; -defparam bootram.RAM4.INIT_0C=256'hff0651d8_05527781_538a3dfc_a1053482_33028405_70810558_8a3d3476_17575473; -defparam bootram.RAM4.INIT_0D=256'h04fa3d0d_0c8a3d0d_81547380_8538c139_3f73802e_8a51da80_81ff0654_d23f8008; -defparam bootram.RAM4.INIT_0E=256'hd051ff89_81f75280_3dfc0553_34815488_5675883d_748338dc_5580de56_02a30533; -defparam bootram.RAM4.INIT_0F=256'h705256d7_02a70533_3dfc0552_34815389_0533893d_7c5702ab_04f93d0d_3f883d0d; -defparam bootram.RAM4.INIT_10=256'h3f800881_7551d6b5_76537b52_77259738_2e9e3880_56547380_81ff0670_f23f8008; -defparam bootram.RAM4.INIT_11=256'h5381f752_883dfc05_3d0d8154_3d0d04fa_74800c89_83388155_5473802e_ff067056; -defparam bootram.RAM4.INIT_12=256'h3d0d0499_75800c88_83388156_2e098106_567480de_883d3356_a03f800b_80d051ff; -defparam bootram.RAM4.INIT_13=256'h0d72882b_0c04803d_0b81c0b0_ac0c89b0_a60b81c0_81c0800c_0c80eb0b_0b81c094; -defparam bootram.RAM4.INIT_14=256'h515170f1_70810651_0870812a_0c81c0a4_0b81c0a0_980c5182_810781c0_be800670; -defparam bootram.RAM4.INIT_15=256'h7381c09c_c0980c51_70810781_2bbe8006_3d0d7288_3d0d0480_08800c82_3881c0a8; -defparam bootram.RAM4.INIT_16=256'h39fa3d0d_3d0d04ff_70f13882_06515151_812a7081_c0a40870_c0a00c81_0c840b81; -defparam bootram.RAM4.INIT_17=256'h38815188_71802e86_72830652_52718a38_38758306_57577191_83065555_787a7c72; -defparam bootram.RAM4.INIT_18=256'h1454e939_52545281_7008720c_77117712_3873822b_73752794_2a725555_ca3f7282; -defparam bootram.RAM4.INIT_19=256'h0680e484_c13f728f_515353d1_84113354_8f0680e4_70842a70_fe3d0d74_883d0d04; -defparam bootram.RAM4.INIT_1A=256'hf138823d_51515170_2a708106_90087088_3d0d82e0_3d0d0480_d1b43f84_11335253; -defparam bootram.RAM4.INIT_1B=256'h70882a70_82e09008_80075353_060780c0_067a8c80_337880ff_0d029305_0d04fe3d; -defparam bootram.RAM4.INIT_1C=256'h800782e0_980c7182_ff0682e0_900c7581_0c7182e0_7682e080_5170f138_81065151; -defparam bootram.RAM4.INIT_1D=256'h08517080_3882e080_515170f1_70810651_0870882a_3882e090_72802e96_900c7251; -defparam bootram.RAM4.INIT_1E=256'h863d0d04_51ff873f_53805280_55885480_940c8880_810b82e0_04fc3d0d_0c843d0d; -defparam bootram.RAM4.INIT_1F=256'h04fc3d0d_0c863d0d_81ff0680_f13f8008_528151fe_8a805381_80559054_fc3d0d88; -defparam bootram.RAM4.INIT_20=256'h06800c82_08813281_0dca3f80_0d04803d_d53f863d_528051fe_54865381_88805588; -defparam bootram.RAM4.INIT_21=256'h84e33f75_3d0d7756_3d0d04fb_2ef43882_06517080_800881ff_3d0deb3f_3d0d0480; -defparam bootram.RAM4.INIT_22=256'hfe843f87_81528051_9b0a0753_fe9b0a06_55a05475_b43f8880_38dd3fff_8008269b; -defparam bootram.RAM4.INIT_23=256'h38751754_ff2681b4_80557381_11565757_cb3d08ff_c93d0880_ba3d0d80_3d0d04ff; -defparam bootram.RAM4.INIT_24=256'h3d085273_755380cb_548c8f3f_883d7052_5381ff52_a7388280_80082681_849f3f73; -defparam bootram.RAM4.INIT_25=256'h0a0680c0_0c76fec0_0b82e090_980c8880_3f7482e0_d43ffd9f_fefd3ffe_518aea3f; -defparam bootram.RAM4.INIT_26=256'h3f80c83d_900cfcef_a00b82e0_e0900c8a_88a00b82_82e0980c_800c810b_0a0782e0; -defparam bootram.RAM4.INIT_27=256'h82e0840c_88157008_880c54fe_700882e0_54fe8415_82e08c0c_80157008_558f56fe; -defparam bootram.RAM4.INIT_28=256'hff169016_0cfcb03f_0b82e090_900c8a80_800b82e0_800c5488_700882e0_54fe8c15; -defparam bootram.RAM4.INIT_29=256'h7b7d7212_f93d0d79_c83d0d04_74800c80_980c8155_800b82e0_25ffbc38_56567580; -defparam bootram.RAM4.INIT_2A=256'h5473802e_7581ff06_2e80c338_81577480_2680cb38_57738008_82db3f80_575a5656; -defparam bootram.RAM4.INIT_2B=256'h19767631_3f731674_7551fdeb_77537352_83387654_57767527_74317555_a2388280; -defparam bootram.RAM4.INIT_2C=256'h0c893d0d_81577680_39fd8c3f_828054dc_7527e138_74548280_802e8e38_57595674; -defparam bootram.RAM4.INIT_2D=256'h0b88160c_27903880_3f800874_135481ed_2e8d3873_54557380_76787a56_04fc3d0d; -defparam bootram.RAM4.INIT_2E=256'h08307276_81bd3f80_16565152_707406ff_3f800830_a63981cb_0c80750c_800b8416; -defparam bootram.RAM4.INIT_2F=256'h0881ff06_fc983f80_3d0d7554_3d0d04fd_fcc93f86_160c7151_160c7188_0c740684; -defparam bootram.RAM4.INIT_30=256'h7088160c_08800805_823f8814_2e943881_08841508_81538814_802e9f38_70545271; -defparam bootram.RAM4.INIT_31=256'h51faa33f_53815281_5481f90a_888055a0_04fc3d0d_0c853d0d_80537280_51fc943f; -defparam bootram.RAM4.INIT_32=256'h81ff0680_08882a70_38d73f80_efe808a0_ff3d0d80_863d0d04_0a06800c_8008fe80; -defparam bootram.RAM4.INIT_33=256'h82712784_ea115252_80efe808_80efe80c_06933871_a02e0981_54515170_0881ff06; -defparam bootram.RAM4.INIT_34=256'h082b800c_3f810b80_800c04f3_e4da0533_3f800880_3d0d04c0_71800c83_38f5b23f; -defparam bootram.RAM4.INIT_35=256'h0b82e090_980c8880_800b82e0_56f9983f_f63d0d7d_2b800c04_810b8008_04ffa93f; -defparam bootram.RAM4.INIT_36=256'ha80b82e0_e0900c8a_88a80b82_82e0980c_800c810b_882b82e0_e0840c7c_0c8b0b82; -defparam bootram.RAM4.INIT_37=256'h0cf8cc3f_0b82e090_900c8a80_800b82e0_80d33888_54737627_3f7e5580_900cf8e7; -defparam bootram.RAM4.INIT_38=256'h53707327_31525790_883d7675_e080085b_84085a82_085982e0_5882e088_82e08c08; -defparam bootram.RAM4.INIT_39=256'h1454ffa9_52ec3972_57348112_75708105_17517033_27913871_80527173_83387053; -defparam bootram.RAM4.INIT_3A=256'h538c088c_fd3d0d80_08028c0c_f7893f8c_3d0d7251_3d0d0480_e0980c8c_39800b82; -defparam bootram.RAM4.INIT_3B=256'h0cfd3d0d_8c08028c_0d8c0c04_0c54853d_80087080_5182de3f_08880508_0508528c; -defparam bootram.RAM4.INIT_3C=256'h048c0802_3d0d8c0c_800c5485_3f800870_085182b9_8c088805_8c050852_81538c08; -defparam bootram.RAM4.INIT_3D=256'h0888050c_0508308c_388c0888_088025ab_8c088805_08fc050c_0d800b8c_8c0cf93d; -defparam bootram.RAM4.INIT_3E=256'hfc050c8c_05088c08_0c8c08f4_8c08f405_8838810b_08fc0508_f4050c8c_800b8c08; -defparam bootram.RAM4.INIT_3F=256'hfc050888_050c8c08_0b8c08f0_8c050c80_08308c08_8c088c05_8025ab38_088c0508; -defparam bootram.RAM5.INIT_00=256'h88050851_08528c08_8c088c05_050c8053_088c08fc_8c08f005_08f0050c_38810b8c; -defparam bootram.RAM5.INIT_01=256'h08f8050c_0508308c_388c08f8_08802e8c_8c08fc05_f8050c54_08708c08_81a73f80; -defparam bootram.RAM5.INIT_02=256'h050c8c08_0b8c08fc_fb3d0d80_08028c0c_8c0c048c_54893d0d_0870800c_8c08f805; -defparam bootram.RAM5.INIT_03=256'h05088025_0c8c088c_8c08fc05_050c810b_308c0888_08880508_2593388c_88050880; -defparam bootram.RAM5.INIT_04=256'h3f800870_050851ad_528c0888_088c0508_0c81538c_8c088c05_8c050830_8c388c08; -defparam bootram.RAM5.INIT_05=256'hf8050870_050c8c08_308c08f8_08f80508_2e8c388c_fc050880_0c548c08_8c08f805; -defparam bootram.RAM5.INIT_06=256'hf8050c8c_800b8c08_08fc050c_0d810b8c_8c0cfd3d_048c0802_3d0d8c0c_800c5487; -defparam bootram.RAM5.INIT_07=256'h2499388c_088c0508_38800b8c_08802ea3_8c08fc05_0827ac38_8c088805_088c0508; -defparam bootram.RAM5.INIT_08=256'h802e80c9_08fc0508_0cc9398c_8c08fc05_fc050810_050c8c08_108c088c_088c0508; -defparam bootram.RAM5.INIT_09=256'h050c8c08_318c0888_088c0508_8805088c_a1388c08_88050826_05088c08_388c088c; -defparam bootram.RAM5.INIT_0A=256'h8c050881_050c8c08_2a8c08fc_fc050881_050c8c08_078c08f8_08fc0508_f805088c; -defparam bootram.RAM5.INIT_0B=256'h0c518d39_8c08f405_88050870_8f388c08_0508802e_398c0890_050cffaf_2a8c088c; -defparam bootram.RAM5.INIT_0C=256'h78777956_04fc3d0d_3d0d8c0c_08800c85_8c08f405_f4050c51_08708c08_8c08f805; -defparam bootram.RAM5.INIT_0D=256'h74335253_a0387433_5271ff2e_b038ff12_5170802e_74078306_278c3874_56528372; -defparam bootram.RAM5.INIT_0E=256'h0c863d0d_38800b80_098106e2_5571ff2e_ff145455_81158115_8106bd38_72712e09; -defparam bootram.RAM5.INIT_0F=256'h38707355_718326e9_14545451_118414fc_068f3884_082e0981_51700873_04747454; -defparam bootram.RAM5.INIT_10=256'h38727507_8f72278c_55555555_7670797b_04fc3d0d_0c863d0d_72713180_55ffaf39; -defparam bootram.RAM5.INIT_11=256'hff125271_81055634_54337470_72708105_ff2e9838_ff125271_802ea738_83065170; -defparam bootram.RAM5.INIT_12=256'h72708405_8405530c_54087170_72708405_0d047451_800c863d_06ea3874_ff2e0981; -defparam bootram.RAM5.INIT_13=256'h8405530c_54087170_72708405_8405530c_54087170_72708405_8405530c_54087170; -defparam bootram.RAM5.INIT_14=256'h718326ed_0cfc1252_70840553_05540871_38727084_83722795_8f26c938_f0125271; -defparam bootram.RAM5.INIT_15=256'h83065170_278a3874_53558372_05335755_028c059f_0d767971_8339fc3d_387054ff; -defparam bootram.RAM5.INIT_16=256'hef387480_2e098106_125271ff_055534ff_73737081_ff2e9338_ff125271_802ea238; -defparam bootram.RAM5.INIT_17=256'h05530c72_72717084_7227a538_5154518f_71902b07_2b750770_04747488_0c863d0d; -defparam bootram.RAM5.INIT_18=256'h83722790_8f26dd38_f0125271_8405530c_0c727170_70840553_530c7271_71708405; -defparam bootram.RAM5.INIT_19=256'h54555552_787a7c70_39fa3d0d_7053ff90_8326f238_fc125271_8405530c_38727170; -defparam bootram.RAM5.INIT_1A=256'h74335651_b1387133_5372ff2e_d438ff13_70802e80_07830651_d9387174_72802e80; -defparam bootram.RAM5.INIT_1B=256'h15ff1555_38811281_802e80fc_ff065170_87387081_72802e81_8106a938_74712e09; -defparam bootram.RAM5.INIT_1C=256'h52527080_71713151_7581ff06_7081ff06_74335651_d1387133_2e098106_555272ff; -defparam bootram.RAM5.INIT_1D=256'hfc135372_52ff9739_38747655_74082e88_88387108_55837327_04717457_0c883d0d; -defparam bootram.RAM5.INIT_1E=256'h15841757_709a3884_06515151_84828180_120670f8_f7fbfdff_74087009_802eb138; -defparam bootram.RAM5.INIT_1F=256'hfd3d0d80_883d0d04_800b800c_52fedf39_38747655_76082ed0_d0387408_55837327; -defparam bootram.RAM5.INIT_20=256'h528151ff_3f80e4f4_3fffafe8_0cffb0cc_7380efec_812e9e38_08545472_0b80e4b4; -defparam bootram.RAM5.INIT_21=256'hffb7c13f_f4528151_cb3f80e4_af3fffaf_ec0cffb0_3f7280ef_0851f6a3_b7de3f80; -defparam bootram.RAM5.INIT_22=256'h2dfc1270_2e913870_525270ff_fc057008_80e4fc0b_39ff3d0d_863f00ff_800851f6; -defparam bootram.RAM5.INIT_23=256'h21457272_00000040_04000000_ffb0da3f_3d0d0404_06f13883_ff2e0981_08525270; -defparam bootram.RAM5.INIT_24=256'h3a204578_646c6572_2068616e_636b6574_6c207061_6e74726f_6e20636f_6f722069; -defparam bootram.RAM5.INIT_25=256'h25642c20_62657220_206e756d_6c697479_74696269_6f6d7061_65642063_70656374; -defparam bootram.RAM5.INIT_26=256'h6c207061_6e74726f_6e20636f_6f722069_21457272_25640a00_676f7420_62757420; -defparam bootram.RAM5.INIT_27=256'h6164206c_61796c6f_65642070_70656374_3a204578_646c6572_2068616e_636b6574; -defparam bootram.RAM5.INIT_28=256'h206c696e_0a657468_0a000000_74202564_7420676f_2c206275_68202564_656e6774; -defparam bootram.RAM5.INIT_29=256'h50204e32_0a555352_640a0000_203d2025_70656564_643a2073_616e6765_6b206368; -defparam bootram.RAM5.INIT_2A=256'h70617469_20636f6d_46504741_720a0000_6f616465_6f6f746c_44502062_31302055; -defparam bootram.RAM5.INIT_2B=256'h20636f6d_77617265_4669726d_640a0000_723a2025_756d6265_7479206e_62696c69; -defparam bootram.RAM5.INIT_2C=256'h00000000_61646472_640a0000_723a2025_756d6265_7479206e_62696c69_70617469; -defparam bootram.RAM5.INIT_2D=256'h00000699_00000000_65743a20_7061636b_65727920_65636f76_69702072_476f7420; -defparam bootram.RAM5.INIT_2E=256'h000006ee_00000705_0000079e_0000079e_0000079e_0000079e_0000079e_0000079e; -defparam bootram.RAM5.INIT_2F=256'h0000079e_0000079e_0000079e_0000079e_0000079e_00000774_0000079e_0000079e; -defparam bootram.RAM5.INIT_30=256'h00000762_00000755_0000074e_00000747_00000742_0000073d_000006a6_00000722; -defparam bootram.RAM5.INIT_31=256'h25642e25_45000000_01b200d9_05160364_14580a2c_3fff0000_0050c285_c0a80a02; -defparam bootram.RAM5.INIT_32=256'hffffffff_00000000_43444546_38394142_34353637_30313233_2e256400_642e2564; -defparam bootram.RAM5.INIT_33=256'h6f66206c_656e7420_69676e6d_6420616c_3a206261_5f706b74_73656e64_ffff0000; -defparam bootram.RAM5.INIT_34=256'h6661696c_6f6e3a20_636f6d6d_6e65745f_66000000_72206275_6e642f6f_656e2061; -defparam bootram.RAM5.INIT_35=256'h00000000_666f7220_696e6720_6c6f6f6b_63686520_74206361_6f206869_65642074; -defparam bootram.RAM5.INIT_36=256'h0a000000_3d202564_697a6520_72642073_20776569_6172703a_646c655f_0a68616e; -defparam bootram.RAM5.INIT_37=256'h2025640a_3a202564_67746873_206c656e_74656e74_6e736973_696e636f_55445020; -defparam bootram.RAM5.INIT_38=256'h61666520_696e2073_50322b20_20555352_74696e67_53746172_0b0b0b0b_00000000; -defparam bootram.RAM5.INIT_39=256'h00000000_6172652e_69726d77_66652066_67207361_6164696e_2e204c6f_6d6f6465; -defparam bootram.RAM5.INIT_3A=256'h6e204650_6374696f_726f6475_69642070_2076616c_20666f72_6b696e67_43686563; -defparam bootram.RAM5.INIT_3B=256'h20465047_74696f6e_6f647563_64207072_56616c69_2e2e2e00_6d616765_47412069; -defparam bootram.RAM5.INIT_3C=256'h20626f6f_6720746f_7074696e_7474656d_642e2041_666f756e_61676520_4120696d; -defparam bootram.RAM5.INIT_3D=256'h20696d61_46504741_696f6e20_64756374_2070726f_616c6964_4e6f2076_742e0000; -defparam bootram.RAM5.INIT_3E=256'h20627569_6820746f_726f7567_67207468_6c6c696e_2e0a4661_6f756e64_67652066; -defparam bootram.RAM5.INIT_3F=256'h74696f6e_6f647563_64207072_56616c69_72652e00_726d7761_6e206669_6c742d69; -defparam bootram.RAM6.INIT_00=256'h46696e69_2e2e2e00_64696e67_204c6f61_756e642e_6520666f_6d776172_20666972; -defparam bootram.RAM6.INIT_01=256'h2e000000_6d616765_6e672069_61727469_2e205374_64696e67_206c6f61_73686564; -defparam bootram.RAM6.INIT_02=256'h72616d21_70726f67_61696e20_6f6d206d_6e206672_65747572_523a2052_4552524f; -defparam bootram.RAM6.INIT_03=256'h4e6f2076_6e210000_61707065_65722068_206e6576_6f756c64_73207368_20546869; -defparam bootram.RAM6.INIT_04=256'h6e642e20_20666f75_77617265_6669726d_696f6e20_64756374_2070726f_616c6964; -defparam bootram.RAM6.INIT_05=256'h6669726d_2d696e20_75696c74_746f2062_75676820_7468726f_696e6720_46616c6c; -defparam bootram.RAM6.INIT_06=256'h4e4f4e45_00000000_2025640a_7420746f_64207365_53706565_2e000000_77617265; -defparam bootram.RAM6.INIT_07=256'h43000000_45545249_53594d4d_58000000_57455f52_58000000_57455f54_00000000; -defparam bootram.RAM6.INIT_08=256'h4155544f_5048595f_6c3a2000_6e74726f_7720636f_20666c6f_726e6574_65746865; -defparam bootram.RAM6.INIT_09=256'h780a0000_20307825_20676f74_7825782c_74652030_2077726f_4144563a_4e45475f; -defparam bootram.RAM6.INIT_0A=256'h64617465_6e207570_6f722069_21457272_00030203_00000001_00030003_00000000; -defparam bootram.RAM6.INIT_0B=256'h796c6f61_64207061_65637465_20457870_6c65723a_68616e64_6b657420_20706163; -defparam bootram.RAM6.INIT_0C=256'h00002042_00000000_2025640a_20676f74_20627574_2025642c_6e677468_64206c65; -defparam bootram.RAM6.INIT_0D=256'h00002102_00002102_00002102_0000207b_0000209d_000020b2_00002102_00002102; -defparam bootram.RAM6.INIT_0E=256'h00002102_00002102_00002102_00002102_00002102_00002102_00002102_00002102; -defparam bootram.RAM6.INIT_0F=256'h6f72740a_0a0a6162_000020ce_0000208d_00002102_00002102_000020f8_000020e1; -defparam bootram.RAM6.INIT_10=256'h65000000_792e6578_64756d6d_43444546_38394142_34353637_30313233_00000000; -defparam bootram.RAM6.INIT_11=256'h00003284_00000000_00000000_00000000_ffffff00_ffff00ff_ff00ffff_00ffffff; -defparam bootram.RAM6.INIT_12=256'h000b0000_0018000f_ffff0031_05050400_01010100_3fff0000_0050c285_c0a80a02; -defparam bootram.RAM6.INIT_13=256'h00000000_ffffffff_00003214_10101200_000030f4_000030ec_000030e4_000030dc; -defparam bootram.RAM6.INIT_14=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_ffffffff; -defparam bootram.RAM6.INIT_15=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_16=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_17=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_18=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_19=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_1A=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_1B=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_1C=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_1D=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_1E=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_1F=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_20=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; -defparam bootram.RAM6.INIT_21=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; +defparam bootram.RAM0.INIT_23=256'h800b80e8_b40c82a0_0b0b80e8_8380800b_822ebd38_80e7e808_802ea438_80e7e408; +defparam bootram.RAM0.INIT_24=256'h0b80e8b8_80808280_e8b40cf8_0b0b0b80_808080a4_bc0c04f8_800b80e8_b80c8290; +defparam bootram.RAM0.INIT_25=256'h940b80e8_80c0a880_80e8b40c_8c0b0b0b_80c0a880_e8bc0c04_84800b80_0cf88080; +defparam bootram.RAM0.INIT_26=256'h70085252_80e7f008_5170a738_80e8c033_04ff3d0d_80e8bc0c_80dca40b_b80c0b0b; +defparam bootram.RAM0.INIT_27=256'hc034833d_810b80e8_5270ee38_08700852_2d80e7f0_e7f00c70_38841280_70802e94; +defparam bootram.RAM0.INIT_28=256'h38823d0d_09810685_800b802e_0b0b0b0b_802e8e38_80e8b008_3d0d0b0b_0d040480; +defparam bootram.RAM0.INIT_29=256'h389d3d22_5b7a80c4_0da13d08_0404e63d_3f823d0d_0b0bf5d4_e8b0510b_040b0b80; +defparam bootram.RAM0.INIT_2A=256'h862e8c38_39798380_8e3882fb_8380842e_248b3879_79838085_852eac38_5a798380; +defparam bootram.RAM0.INIT_2B=256'h0c82d439_7a80e8c4_81e4d00c_0c86397a_0b81e2cc_0c8e3981_0b81e18c_82f13981; +defparam bootram.RAM0.INIT_2C=256'h82b53979_842e9f38_38798380_8085248b_a7387983_8380852e_225b4079_800ba03d; +defparam bootram.RAM0.INIT_2D=256'h4288539d_83409a3d_81408339_82408739_3882a439_80872e8c_8b387983_8380862e; +defparam bootram.RAM0.INIT_2E=256'h9d3df405_963d7053_80cbe43f_3d526251_4388539d_f23f983d_615180cb_3d880552; +defparam bootram.RAM0.INIT_2F=256'h3f800808_68458cde_22993d23_08498e1b_fb38881b_3d082780_3f8f0ba3_5241acb8; +defparam bootram.RAM0.INIT_30=256'h9c3de405_08456052_cf3f8008_2e88388c_5b5b7a7a_06657b06_8008087a_5a8ccd3f; +defparam bootram.RAM0.INIT_31=256'hff5b7a44_51a0da3f_9c3de405_81068b38_797b2e09_065a805b_800881ff_51abf93f; +defparam bootram.RAM0.INIT_32=256'h887c26ed_34811c5c_5b79337b_3d7d055b_5c7b1d9e_0b833d5e_e0055480_84559c3d; +defparam bootram.RAM0.INIT_33=256'h621d5b5b_5e5c7b1d_800b883d_615b5f5f_08028405_8bdc3f80_519ff53f_389f3d22; +defparam bootram.RAM0.INIT_34=256'h811c5c88_79337b34_631d5b5b_5e5c7b1d_800b901f_7c26ef38_811c5c88_79337b34; +defparam bootram.RAM0.INIT_35=256'h805c7b1e_7c26ef38_811c5c86_79337b34_7f1d5b5b_5e5c7b1d_800b881f_7c26ef38; +defparam bootram.RAM0.INIT_36=256'h84120859_3d0d686a_3d0d04ee_9af13f9c_7c26ef38_811c5c86_79337b34_611d5b5b; +defparam bootram.RAM0.INIT_37=256'h95e93f80_80dca851_75538c52_802e8c38_2e943875_0856758c_279c3877_5a588379; +defparam bootram.RAM0.INIT_38=256'hff9f1756_8818085d_8c5ba05c_d63fa057_dcf85195_53a45280_268e3878_e15778a3; +defparam bootram.RAM0.INIT_39=256'heb399518_08085e81_8a9a3f80_0480c15c_05567508_2980dee8_81387584_75922682; +defparam bootram.RAM0.INIT_3A=256'h97193357_0852800b_08538c18_33549018_76559618_83388457_7580f22e_33568257; +defparam bootram.RAM0.INIT_3B=256'h3dea0553_33705495_b3398d18_80d35c81_3f80085f_76519791_83388157_5775772e; +defparam bootram.RAM0.INIT_3C=256'h3f80c85c_52568ecc_538c1933_70548e19_398d1833_80c95c94_568dbf3f_8c193352; +defparam bootram.RAM0.INIT_3D=256'h058c1908_2980dfb4_c2387584_75852680_33ff0556_ff399418_b5053480_75028405; +defparam bootram.RAM0.INIT_3E=256'h39768429_2277239b_a2399218_1808770c_5fa93990_ae397622_0476085f_58567508; +defparam bootram.RAM0.INIT_3F=256'h785e80cc_d25cad39_710c5680_05901908_2980e8c4_8e397684_70084056_80e8c405; +defparam bootram.RAM1.INIT_00=256'h81185888_75337734_79055757_7719963d_833d5a58_0554800b_55943ddc_5c8c1808; +defparam bootram.RAM1.INIT_01=256'h57753377_3d790557_58771996_0b833d5a_dc055480_a455943d_a439a05c_7826ed38; +defparam bootram.RAM1.INIT_02=256'hc4525393_705380dd_fe3d0d74_943d0d04_519c8d3f_38838080_887826ed_34811858; +defparam bootram.RAM1.INIT_03=256'h88b33f72_a0527251_a33f8d39_90b83f9d_c43f8151_52a05188_2e9238a0_863f7280; +defparam bootram.RAM1.INIT_04=256'h805192c7_8a5280de_5189a43f_3f80dde4_973fa5de_fa3d0d82_843d0d04_5190a73f; +defparam bootram.RAM1.INIT_05=256'h90fc3f88_3f800851_e23f878e_abe53f87_80e8e00c_be3f820b_dea05192_3f8c5280; +defparam bootram.RAM1.INIT_06=256'h95b83f86_52800851_86f03f73_3f800854_b53f8780_91963f88_3f800851_c13f878c; +defparam bootram.RAM1.INIT_07=256'h83808451_3f8ab252_805195fe_d7528380_95c23f8d_518f873f_85528008_ef3f8380; +defparam bootram.RAM1.INIT_08=256'h875195d6_b2528380_95e03f8a_83808551_3f8ab252_865195ea_b2528380_95f43f8a; +defparam bootram.RAM1.INIT_09=256'h8e3faea0_90d451ac_518ef33f_a7bb3f80_83809251_5195cb3f_52838082_3f80c3cf; +defparam bootram.RAM1.INIT_0A=256'h82fdee2e_22555573_80088e05_80c93876_8008802e_3f800856_05518eea_3f883dfc; +defparam bootram.RAM1.INIT_0B=256'h90e93f94_80decc51_80089a38_80c6f03f_08900551_dec45280_38845380_098106ad; +defparam bootram.RAM1.INIT_0C=256'h8e3f8ce1_8def3fa7_519c863f_39745275_85e03f88_f93f7351_8fda3f86_16705254; +defparam bootram.RAM1.INIT_0D=256'h51868f3f_3f9f5280_963f88e6_87ea3f8c_3f82b73f_c23f92b1_fe3d0d86_3fff9e39; +defparam bootram.RAM1.INIT_0E=256'h885185d1_c83f8852_82ac518b_5185de3f_3f845284_ac518bd5_85eb3f82_9f528051; +defparam bootram.RAM1.INIT_0F=256'h3f80e451_805185b5_82539f52_518bae3f_c43f82ac_52905185_8bbb3f90_3f82ac51; +defparam bootram.RAM1.INIT_10=256'h85b03f9f_9f529e51_8025df38_ff135372_518b923f_a83f80e4_529c5185_8b9f3f9f; +defparam bootram.RAM1.INIT_11=256'h8b2a8106_808c0870_803d0d82_843d0d04_810b800c_81e0840c_8c3f890b_52815185; +defparam bootram.RAM1.INIT_12=256'h57585957_82055a57_30708025_a7053370_7f028c05_3d0d7a7d_3d0d04f9_800c5182; +defparam bootram.RAM1.INIT_13=256'h51538054_30709f2a_388a5573_88557383_832e8838_88055575_38728025_75822e93; +defparam bootram.RAM1.INIT_14=256'h54548054_842b0751_fe057072_05777131_76812cff_802e9738_38725472_8177259e; +defparam bootram.RAM1.INIT_15=256'h51b0b43f_73528118_51b0bc3f_ff065277_c63f7281_527b51b0_81805474_86397353; +defparam bootram.RAM1.INIT_16=256'h527551fe_54bd5378_815580ca_9f053356_fb3d0d02_893d0d04_51b0ac3f_815280da; +defparam bootram.RAM1.INIT_17=256'h81ff0653_bb3f8008_51d63ffe_3f815281_c551b083_0d815280_0d04fe3d_e63f873d; +defparam bootram.RAM1.INIT_18=256'h81705654_787a5757_04fa3d0d_53843d0d_81e0800c_08709007_3881e080_72802ef3; +defparam bootram.RAM1.INIT_19=256'h38805581_81ff2e83_71335271_83388054_5270802e_17703352_279e3872_80537276; +defparam bootram.RAM1.INIT_1A=256'h33557481_0d80e8e4_0d04fc3d_800c883d_38815170_70802e83_74740751_1353df39; +defparam bootram.RAM1.INIT_1B=256'h74802e95_81ff0655_9e3f8008_80d051b0_e8538252_865480e8_80e8e434_e138810b; +defparam bootram.RAM1.INIT_1C=256'he8e85180_dfcc5280_38865380_74802e8f_81ff0655_8c3f8008_e8e851ff_38865280; +defparam bootram.RAM1.INIT_1D=256'h845280e8_802e9538_ff065574_3f800881_d051afe3_538c5280_5480e8f0_c0993f84; +defparam bootram.RAM1.INIT_1E=256'hf453b852_845480e8_80e8f00c_80dfd408_802e8938_ff065574_3f800881_f051fed1; +defparam bootram.RAM1.INIT_1F=256'h81ff0655_9c3f8008_e8f451fe_38845280_74802e95_81ff0655_ae3f8008_80d051af; +defparam bootram.RAM1.INIT_20=256'h0881ff06_aef93f80_5280d051_e8f85388_0c845480_0880e8f4_3880dfd8_74802e89; +defparam bootram.RAM1.INIT_21=256'hdc0880e8_893880df_5574802e_0881ff06_fde73f80_80e8f851_95388452_5574802e; +defparam bootram.RAM1.INIT_22=256'h3f80e8f8_0c04fdfa_e8f00b80_fe843f80_0b800c04_3f80e8e8_0d04fe8e_f80c863d; +defparam bootram.RAM1.INIT_23=256'had993f80_5280d051_5475538c_0d775684_0c04fb3d_e8f40b80_fdf03f80_0b800c04; +defparam bootram.RAM1.INIT_24=256'h80e8e434_3d0d810b_3d0d04fe_74800c87_80e8f00c_87387508_5574802e_0881ff06; +defparam bootram.RAM1.INIT_25=256'hfc080607_067180e8_73097375_04803d0d_3f843d0d_e851be8f_cc5280e8_945380df; +defparam bootram.RAM1.INIT_26=256'h077080e9_e9800806_75067180_0d730973_0d04803d_0c51823d_0c81e08c_7080e8fc; +defparam bootram.RAM1.INIT_27=256'h800c843d_81c73f72_53538051_3d0d7470_af3f04fe_3d0d0481_980c5182_800c81e0; +defparam bootram.RAM1.INIT_28=256'h06545472_337081ff_79565674_fb3d0d77_833d0d04_5181b63f_0d8a5280_0d04ff3d; +defparam bootram.RAM1.INIT_29=256'h3d0d7352_3d0d04ff_0b800c87_3fe53980_52558191_ff065376_81157481_802e9038; +defparam bootram.RAM1.INIT_2A=256'h0b800c84_80e73f80_8a527251_53ffbd3f_76537052_fe3d0d74_833d0d04_8051cd3f; +defparam bootram.RAM1.INIT_2B=256'h1234823d_3380e7f4_51028f05_803d0d72_833d0d04_8051dd3f_3d0d7352_3d0d04ff; +defparam bootram.RAM1.INIT_2C=256'hfe3d0d80_833d0d04_720c5351_e0057022_751080df_82908005_0d73a029_0d04ff3d; +defparam bootram.RAM1.INIT_2D=256'h38843d0d_827325e5_3f811353_527251ce_e7f81333_51c63f80_13335272_5380e7f4; +defparam bootram.RAM1.INIT_2E=256'h87388d52_2e098106_33537281_80e7f414_81069538_748a2e09_76785654_04fc3d0d; +defparam bootram.RAM1.INIT_2F=256'h04fe3d0d_0c863d0d_38748c15_72802ef8_84140853_90800554_73a02982_7351de3f; +defparam bootram.RAM1.INIT_30=256'h0d04ff3d_800c843d_12085372_2e853890_ff537080_11085252_90800588_74a02982; +defparam bootram.RAM1.INIT_31=256'h800b81a8_840c5181_882a81a8_a8800c70_81ff0681_e8802270_a8880c80_0d800b81; +defparam bootram.RAM1.INIT_32=256'h81a89008_2e818638_81517180_33555354_88059705_0d767802_0d04fd3d_880c833d; +defparam bootram.RAM1.INIT_33=256'ha8900870_a8900c81_81900b81_81a88c0c_72108107_5170f138_81065151_70862a70; +defparam bootram.RAM1.INIT_34=256'h70802eba_51515151_06708132_872a7081_a8900870_70f13881_06515151_812a7081; +defparam bootram.RAM1.INIT_35=256'h70810651_0870812a_0c81a890_7081a890_8338a051_5171812e_b13880e8_3871802e; +defparam bootram.RAM1.INIT_36=256'h0b81a890_883980c0_cc398151_34ff1252_70810556_08517074_3881a88c_515170f1; +defparam bootram.RAM1.INIT_37=256'h2a708106_90087086_535481a8_97053355_78028805_fd3d0d76_853d0d04_0c70800c; +defparam bootram.RAM1.INIT_38=256'h81a89008_81a8900c_81905170_802e8438_81d05171_81a88c0c_f1387210_51515170; +defparam bootram.RAM1.INIT_39=256'h5170802e_32515151_81067081_70872a70_81a89008_5170f138_81065151_70812a70; +defparam bootram.RAM1.INIT_3A=256'h900c81a8_517081a8_2e833890_d0517181_a88c0c80_38733381_802e80c5_80cf3871; +defparam bootram.RAM1.INIT_3B=256'h51515170_70813251_2a708106_90087087_f13881a8_51515170_2a708106_90087081; +defparam bootram.RAM1.INIT_3C=256'h0c853d0d_80517080_81a8900c_3980c00b_3981518a_5354ffb7_8114ff13_802e8e38; +defparam bootram.RAM1.INIT_3D=256'h8d9f7127_31515186_ac087073_085281b8_3881b8ac_7274259b_75548053_04fd3d0d; +defparam bootram.RAM1.INIT_3E=256'h0b828084_80800cef_81e20b82_8280880c_3d0dff0b_3d0d04ff_53e23985_f1388113; +defparam bootram.RAM1.INIT_3F=256'h38833d0d_708025f1_0cff1151_70840554_51a0f172_f3a85287_808c0c80_0cff0b82; +defparam bootram.RAM2.INIT_00=256'h74760652_f3a85555_53810b80_58515280_8c087106_70098280_82808808_04fb3d0d; +defparam bootram.RAM2.INIT_01=256'h877325dc_10575553_13841576_0c8f3981_7482808c_0852712d_38725173_71802e8f; +defparam bootram.RAM2.INIT_02=256'h2b700982_0c518172_a8057571_842980f3_269f3871_73527187_04ff3d0d_38873d0d; +defparam bootram.RAM2.INIT_03=256'h81e0c40c_22747008_0d029205_0404ff3d_52833d0d_880c5351_72068280_80880870; +defparam bootram.RAM2.INIT_04=256'h802ef338_06515170_a0087084_cc0c81b8_810b81e0_04803d0d_0c833d0d_5281e0c8; +defparam bootram.RAM2.INIT_05=256'h54527280_08708106_0d81b8a0_0c04fe3d_7181e0c0_0d04de3f_cc0c823d_820b81e0; +defparam bootram.RAM2.INIT_06=256'h5271802e_70810651_3971812a_8080529a_0c535381_71902a71_b8a00875_2e933881; +defparam bootram.RAM2.INIT_07=256'h7080c006_81b8a008_04803d0d_0c843d0d_72527180_3fff9e3f_ec51f8d3_8b3880df; +defparam bootram.RAM2.INIT_08=256'h0781e0cc_70902b88_028e0522_04ff3d0d_0c823d0d_80800b80_2ef23881_51517080; +defparam bootram.RAM2.INIT_09=256'h0d755480_0d04fd3d_cc0c833d_840b81e0_802ef338_06515170_a0087090_0c5281b8; +defparam bootram.RAM2.INIT_0A=256'h853d0d04_7327e638_81135385_52a6ff3f_14703352_f7a53f72_8638ba51_5372802e; +defparam bootram.RAM2.INIT_0B=256'h3d0d04f6_80ed3f87_80dff051_70335356_81113354_82113355_83113356_fb3d0d77; +defparam bootram.RAM2.INIT_0C=256'h3875802e_7680258f_5d5b5957_2a515b5f_7030709f_05bb0533_61630290_3d0d7c7e; +defparam bootram.RAM2.INIT_0D=256'h3f800851_7651aec5_80537752_79557854_77269438_76305777_ad51782d_8a387952; +defparam bootram.RAM2.INIT_0E=256'h8b053351_803d0d02_8c3d0d04_3351782d_80dffc05_dd3f8008_527651ae_ffbd3f77; +defparam bootram.RAM2.INIT_0F=256'h06575775_337081ff_5c5a5878_5208a3d8_70708405_3d0d8c3d_3d0d04f7_f68d3f82; +defparam bootram.RAM2.INIT_10=256'h2e80fb38_597580f0_19703357_80db3881_2e098106_065675a5_387681ff_802e81d1; +defparam bootram.RAM2.INIT_11=256'h80e42e80_81953975_2e819e38_8a387580_7580e324_e32eb938_a0387580_7580f024; +defparam bootram.RAM2.INIT_12=256'h80f82eba_80f53975_2e80db38_387580f3_80f5248b_2eac3875_397580f5_c638818b; +defparam bootram.RAM2.INIT_13=256'h59568055_19710852_da397784_51792d80_56805275_12335259_77841983_3880ec39; +defparam bootram.RAM2.INIT_14=256'h19710852_92397784_81538a52_55a3d854_52595680_84197108_53903977_a3d85480; +defparam bootram.RAM2.INIT_15=256'h5675802e_59567633_19710859_9e397784_51fdd03f_53905275_a3d85480_59568055; +defparam bootram.RAM2.INIT_16=256'h048a0b81_0c8b3d0d_39800b80_1959fea3_2dec3981_58335179_76708105_8e388052; +defparam bootram.RAM2.INIT_17=256'h04fc3d0d_38823d0d_515170ef_70810651_8c2a8132_b8b40870_803d0d81_e0d00c04; +defparam bootram.RAM2.INIT_18=256'h0a075272_86387181_5570802e_55555654_07728106_72982b7b_059b0533_797b0288; +defparam bootram.RAM2.INIT_19=256'h7181e0d4_51ffa93f_79712b51_52a07531_71820a07_802e8638_06515170_822a7081; +defparam bootram.RAM2.INIT_1A=256'hfc3d0d76_863d0d04_5170800c_81b88008_38ff953f_73802e89_d80c7351_0c7081e0; +defparam bootram.RAM2.INIT_1B=256'h2a517080_ee397190_15555351_22730581_72101570_74278f38_55805372_787a5455; +defparam bootram.RAM2.INIT_1C=256'h5280e988_0d865375_0d04fd3d_800c863d_52ec3971_72902a05_83ffff06_2e8d3871; +defparam bootram.RAM2.INIT_1D=256'h8812ff12_5180720c_e9985289_ff3d0d80_853d0d04_e9900c54_76700880_51b0983f; +defparam bootram.RAM2.INIT_1E=256'h5472742e_52527022_0b80e994_05225380_3d0d0296_3d0d04fd_25f33883_52527080; +defparam bootram.RAM2.INIT_1F=256'h83ffff06_0d787a71_0d04fa3d_800c853d_38805170_897225ee_88125252_8e388112; +defparam bootram.RAM2.INIT_20=256'h55555573_0b80e994_0880e998_0cad3980_80088405_2e893876_3f800880_535856c7; +defparam bootram.RAM2.INIT_21=256'h84140c88_75732376_389dad3f_897525eb_88145455_8f388115_5271802e_08881555; +defparam bootram.RAM2.INIT_22=256'h913ddc05_3d880552_3f735392_5254aee7_923dd605_54933d53_3d0d8670_3d0d04f1; +defparam bootram.RAM2.INIT_23=256'h8c3d2381_0523800b_028405a6_8b3d2380_818a800b_05a20523_90800284_51aed83f; +defparam bootram.RAM2.INIT_24=256'h8a52913d_665e8053_0523685d_028405ae_8d3d2380_80c0910b_05aa0523_80800284; +defparam bootram.RAM2.INIT_25=256'h23800b91_8405ba05_963d2202_22903d23_0523983d_028405ae_b73f8008_e40551fd; +defparam bootram.RAM2.INIT_26=256'h913d0d04_519fb03f_81e68405_6980c029_3dd40552_23ac5391_8405be05_3d238002; +defparam bootram.RAM2.INIT_27=256'h529a3df8_5380e988_adc53f86_3df20551_9b3d529a_3d238653_5b800b97_e83d0d80; +defparam bootram.RAM2.INIT_28=256'h9b3dc411_585a800b_80088008_23f7e23f_0580e205_05220284_3f0280f2_0551adb7; +defparam bootram.RAM2.INIT_29=256'h8305fc06_5fa33d08_5ea13d08_5c905d6e_08465984_0845a33d_6e44a13d_43f00543; +defparam bootram.RAM2.INIT_2A=256'h0c750854_84387376_55737527_3151565a_31908071_701a787c_84587508_408c3d56; +defparam bootram.RAM2.INIT_2B=256'he63f7508_e09851ef_2e883880_06547380_94160883_54738c38_38738306_73802e9a; +defparam bootram.RAM2.INIT_2C=256'hbf268438_ffac3878_57778025_17ff1959_84055708_823f7570_5276519e_53941608; +defparam bootram.RAM2.INIT_2D=256'h7f1f9405_0b943d23_40818a80_0d6b6e40_0d04ea3d_e83f9a3d_822a51f6_80c05978; +defparam bootram.RAM2.INIT_2E=256'h075a7996_6980c080_80ce0523_80028405_3d238180_23800b95_0580ca05_5a790284; +defparam bootram.RAM2.INIT_2F=256'h3f800809_525cfae0_52933d70_4780538a_90084668_052380e9_840580d2_3d238002; +defparam bootram.RAM2.INIT_30=256'h79923880_81ff065a_873f8008_535c5e8c_53983d70_23913d70_0580d205_5a790284; +defparam bootram.RAM2.INIT_31=256'h7b549080_575d9455_5960586b_027f5a6d_d53fa939_f6b63fed_c23f7a51_e0c451f7; +defparam bootram.RAM2.INIT_32=256'hf73d0d7f_983d0d04_38fd893f_867c26ef_34811c5c_5b79337b_1d7c1f5b_53805c7b; +defparam bootram.RAM2.INIT_33=256'ha6052377_80028405_768b3d23_23881857_8405a205_8d3d2202_228a3d23_5802ae05; +defparam bootram.RAM2.INIT_34=256'h23908002_810b8e3d_04ee3d0d_3f8b3d0d_7d51fe9e_05539152_548b3df8_567e5588; +defparam bootram.RAM2.INIT_35=256'h53800852_eb8c3f86_05b60523_34810284_8405b505_3d348402_23860b8f_8405b205; +defparam bootram.RAM2.INIT_36=256'h943df605_86538052_51a9e43f_943df205_53800852_eb863f84_51a9f43f_943dec05; +defparam bootram.RAM2.INIT_37=256'h53805b7a_05549086_55943de4_5780569c_59805880_5a025c80_64700844_51aaf13f; +defparam bootram.RAM2.INIT_38=256'h0b8e3d23_ee3d0d81_943d0d04_38fbcd3f_867b26ef_34811b5b_901b337a_1c5a80e0; +defparam bootram.RAM2.INIT_39=256'h893f8653_b60523ea_81028405_05b50534_34840284_860b8f3d_05b20523_90800284; +defparam bootram.RAM2.INIT_3A=256'h53805294_a8e13f86_3df20551_80085294_833f8453_a8f13fea_3dec0551_80085294; +defparam bootram.RAM2.INIT_3B=256'he4055490_9c55943d_80578056_80598058_0843025c_e73f8008_a9ee3fe9_3df60551; +defparam bootram.RAM2.INIT_3C=256'h04d83d0d_3f943d0d_ef38fac8_5b867b26_7a34811b_e0901b33_7a1c5a80_8653805b; +defparam bootram.RAM2.INIT_3D=256'h5c799b26_29f2055b_ad3d0884_cc38901d_09810682_7e90862e_1122405d_ab3d088e; +defparam bootram.RAM2.INIT_3E=256'h225a7990_9138821c_09810687_5a79812e_9d397b22_f49d3f87_80e0f451_8d387952; +defparam bootram.RAM2.INIT_3F=256'h2e098106_225a7982_f538861c_09810686_798c842e_841c225a_06878338_802e0981; +defparam bootram.RAM3.INIT_00=256'h79527a51_51a7a03f_981d5279_3d5a8653_a7ad3fa8_1d527a51_5b84539e_a238943d; +defparam bootram.RAM3.INIT_01=256'h5b888c3f_a81d7052_4088943f_9e1d7052_0686bb38_812e0981_1c225a79_87953f86; +defparam bootram.RAM3.INIT_02=256'h5a865380_9138a83d_5e800886_f63f8008_a40551a5_52aa3dff_5380e990_80084384; +defparam bootram.RAM3.INIT_03=256'h34851c33_1c33a33d_82052384_02840581_23821c22_7b22a23d_51a6d43f_e9885279; +defparam bootram.RAM3.INIT_04=256'h70547b53_a6a13f84_3de40551_537952aa_86052386_02840581_85053482_02840581; +defparam bootram.RAM3.INIT_05=256'haa3df405_79537f52_51a6843f_981d527a_055b8653_3f028192_525aa693_ab3dea05; +defparam bootram.RAM3.INIT_06=256'haa3ddc05_575d9c55_597e587e_027e5a7e_51a5ec3f_537a527f_9f3d4086_51a5f83f; +defparam bootram.RAM3.INIT_07=256'h397e9080_e73f84ee_26ef38f7_1c5c867c_337b3481_1d5b5b79_5c7b1d60_547e537d; +defparam bootram.RAM3.INIT_08=256'h2a708f06_d1387988_09810684_5b60842e_8c2a435b_1d702270_84e43890_2e098106; +defparam bootram.RAM3.INIT_09=256'he0905282_5e865380_84b4387e_ff065f7e_1b2280ff_84c03886_2e098106_515a7985; +defparam bootram.RAM3.INIT_0A=256'h08833881_a3ed3f80_70535b5c_80e99054_901c6255_38815e7e_3f800883_1d51a483; +defparam bootram.RAM3.INIT_0B=256'h5d407f81_22ec1140_1b33821c_84c53f89_529c1d51_8138881d_7b802e84_5c7d8738; +defparam bootram.RAM3.INIT_0C=256'h7a2e8f38_5d42407d_8411225d_7a08a41f_388c1b08_810683de_7f912e09_2e81bb38; +defparam bootram.RAM3.INIT_0D=256'h08428008_f4913f80_22535d5d_e41d821d_bd39ac1d_f0bd3f83_80e19451_79537d52; +defparam bootram.RAM3.INIT_0E=256'h3d408853_a3e13f9d_7d527951_5f5a8853_9b3d9a3d_3d237f4a_387a229a_802e83a6; +defparam bootram.RAM3.INIT_0F=256'h88537952_51a3c03f_b4055279_53aa3dff_23604888_1b22983d_a3d53f82_79527f51; +defparam bootram.RAM3.INIT_10=256'h7c26ef38_811c5c88_79337b34_7c1f5b5b_5e5c7b1d_557e843d_3f7b567c_7d51a3b7; +defparam bootram.RAM3.INIT_11=256'had398c1b_5a792d82_61840508_7b26ef38_811b5b88_84051c34_5a793302_805b7f1b; +defparam bootram.RAM3.INIT_12=256'h2e098106_335a7983_9539811a_81bb3882_387d882e_7d832e8a_33405b42_08a41e70; +defparam bootram.RAM3.INIT_13=256'h3f800841_2251f2cf_81f4387c_2e098106_5e5c7991_8912335c_1d80c01e_81a238ac; +defparam bootram.RAM3.INIT_14=256'h7d51a29b_88537a52_9c3d5c5e_794c993d_229c3d23_1c085a7c_80fe388c_8008802e; +defparam bootram.RAM3.INIT_15=256'h3dcc0552_4e8853aa_9e3d2379_5a821d22_3f901c08_7f51a28f_88537d52_3f973d40; +defparam bootram.RAM3.INIT_16=256'h5b79337b_1d7c1f5b_3d5e5c7b_7e557e84_ee3f7e56_527d51a1_3f88537a_7a51a1f7; +defparam bootram.RAM3.INIT_17=256'h38608405_887b26ef_34811b5b_0284051c_1b5a7933_38805b7f_887c26ef_34811c5c; +defparam bootram.RAM3.INIT_18=256'hd105347e_02840580_963d347e_1d5d5d7e_39ac1de4_e63f80de_80e951e3_085a792d; +defparam bootram.RAM3.INIT_19=256'h3d70525b_53605295_d605237e_02840580_23861a22_1a22973d_d2052384_02840580; +defparam bootram.RAM3.INIT_1A=256'h567c557d_d205237b_02840580_08095a79_f08e3f80_2a527c51_08537b81_f09a3f80; +defparam bootram.RAM3.INIT_1B=256'h3d0d800b_e80c04fc_800b80e9_80e9e40c_0d04800b_c53faa3d_526151f4_547a537f; +defparam bootram.RAM3.INIT_1C=256'h2e098106_53517075_71088c13_ec545651_700880e9_27a43876_55537274_80e9e408; +defparam bootram.RAM3.INIT_1D=256'h71535755_3d0d7779_3d0d04fb_70800c86_e738ff51_53737326_8b398113_85387251; +defparam bootram.RAM3.INIT_1E=256'he9e80881_0c8e3980_1480e9e4_26893881_08547387_3880e9e4_088025ba_ffb93f80; +defparam bootram.RAM3.INIT_1F=256'he9f01451_53755280_0c515486_80e9ec12_822b7608_73101470_e9e80c54_11870680; +defparam bootram.RAM3.INIT_20=256'hfd3d0d75_873d0d04_519fa43f_80e9f005_52738429_54865375_10800805_94398008; +defparam bootram.RAM3.INIT_21=256'h5276519e_80e9f005_53738429_08055486_80081080_08249938_80547380_51fed83f; +defparam bootram.RAM3.INIT_22=256'h14337088_902b0782_71982b71_33811233_3d0d7570_3d0d04fd_73800c85_fa3f8154; +defparam bootram.RAM3.INIT_23=256'h22565957_7f80eacc_f93d0d7d_853d0d04_54565452_800c5253_16337107_2b720783; +defparam bootram.RAM3.INIT_24=256'h902980ea_90291470_80d33873_54738326_72315256_8b3d2270_83ffff06_76a83873; +defparam bootram.RAM3.INIT_25=256'h38749029_748326ad_31575754_3d227072_ffff068d_c0397383_76742380_d0055154; +defparam bootram.RAM3.INIT_26=256'h33535474_38751770_75782791_ea3f8056_d005519d_902980ea_8a3d5273_15548853; +defparam bootram.RAM3.INIT_27=256'hd0545480_800b80ea_80eacc23_029a0522_04fc3d0d_39893d0d_811656ec_51e1de3f; +defparam bootram.RAM3.INIT_28=256'h14829014_eddc3f81_22740551_5280eacc_140cb8f4_800b828c_8288140c_7323800b; +defparam bootram.RAM3.INIT_29=256'h06515675_81327081_5c847c2c_80ead05a_3d0d800b_3d0d04f4_27d93886_54548374; +defparam bootram.RAM3.INIT_2A=256'h08ff2e80_e1a33f80_055b7b51_38781a88_ff2680d6_5b5d7981_82881a08_81be3875; +defparam bootram.RAM3.INIT_2B=256'h58587680_53515951_71802507_30728025_728d3270_8a327030_81ff0670_c5388008; +defparam bootram.RAM3.INIT_2C=256'h811a5a81_828c1a0c_1a0c800b_81058288_82881908_81055d34_5d777b70_2e833881; +defparam bootram.RAM3.INIT_2D=256'h568b7627_828c1b0c_19088111_9138828c_80d2387c_1908802e_b1388288_ff7a27ff; +defparam bootram.RAM3.INIT_2E=256'h57577533_771a781a_833d5b58_1954800b_19085588_ab388288_5675802e_bf387822; +defparam bootram.RAM3.INIT_2F=256'h828c1a0c_1a0c800b_800b8288_51f0e93f_cc227c05_ef3880ea_58887826_77348118; +defparam bootram.RAM3.INIT_30=256'h685194ba_5780c052_883d7054_04ea3d0d_388e3d0d_7c27fea9_1a5a5c83_811c8290; +defparam bootram.RAM3.INIT_31=256'h94387416_2e098106_387381aa_81ff2e9d_33515473_55741770_059d0557_3f800284; +defparam bootram.RAM3.INIT_32=256'h800c983d_38805473_be7527d1_39811555_3881548b_09810685_7381992e_70335154; +defparam bootram.RAM3.INIT_33=256'hca3f8008_52735199_5380e1b8_3f805584_795193ea_54548452_0d863d70_0d04f93d; +defparam bootram.RAM3.INIT_34=256'h3f8ac23f_3d0d8dd7_940c04fc_810b81e0_893d0d04_5574800c_06833881_752e0981; +defparam bootram.RAM3.INIT_35=256'he1bc5181_73883880_06515154_8d2a7081_b8b40870_89c13f81_06558051_800881ff; +defparam bootram.RAM3.INIT_36=256'h51dd843f_3880e2a0_08802e9a_febf3f80_b0800a51_51dd983f_3880e1f4_833974b5; +defparam bootram.RAM3.INIT_37=256'h51fee33f_3998800a_d85180cc_b53f80e2_800a5184_e1b73fb0_3f82ac51_81518987; +defparam bootram.RAM3.INIT_38=256'h80e3d051_5192bf3f_5298800a_5380ffff_3f838080_a451dcd7_bb3880e3_8008802e; +defparam bootram.RAM3.INIT_39=256'he93f8839_3f805183_ac51e0e9_dcb13f82_80e3f451_3ffee53f_ac51e0f9_dcc13f82; +defparam bootram.RAM3.INIT_3A=256'he5893fa0_e4fc5254_75705380_04fd3d0d_80f3900c_3d0d0471_dc9d3f86_80e4b051; +defparam bootram.RAM3.INIT_3B=256'h528051da_fe3d0da0_853d0d04_7351722d_802e8538_90085372_cc3f80f3_52a051da; +defparam bootram.RAM3.INIT_3C=256'hff0b8008_51898d3f_fc3d0d9a_843d0d04_8051722d_802e8538_90085372_b03f80f3; +defparam bootram.RAM3.INIT_3D=256'h7182802e_5580e454_86800653_820b8008_2e80ec38_81557180_06515354_862a7081; +defparam bootram.RAM3.INIT_3E=256'h845188c8_8338ff54_7184802e_3987e854_2e8e388a_8a547180_80248a38_9b387182; +defparam bootram.RAM3.INIT_3F=256'h7080f39c_e5f41133_06720780_8a2c7083_8c068008_3f71882a_855188c0_3f800852; +defparam bootram.RAM4.INIT_00=256'h80f39408_dac93f74_11085252_0680e888_71822b8c_52dab03f_55535154_0c80e5b4; +defparam bootram.RAM4.INIT_01=256'h2e098106_9e397482_38fec13f_098106a3_3874812e_74822ea6_80f3940c_2e983874; +defparam bootram.RAM4.INIT_02=256'h863d0d04_5187cd3f_fdfb3f99_a73f7351_f3980cfe_8e387380_f398082e_96387380; +defparam bootram.RAM4.INIT_03=256'h3f8d5298_995187ac_80f3980c_940cff0b_800b80f3_5187a23f_e13f8008_fd3d0dd7; +defparam bootram.RAM4.INIT_04=256'h908007f4_8f3f8008_3f845187_8451dfb1_3fbf9452_9c5187cd_81ae8052_5187d63f; +defparam bootram.RAM4.INIT_05=256'hcc51e2a7_735280e5_38800853_80082e8d_86fa3f73_b03f8451_54845187_9f067053; +defparam bootram.RAM4.INIT_06=256'h70852a82_02970533_04fd3d0d_3f853d0d_80518789_84800752_e33f8008_3f805186; +defparam bootram.RAM4.INIT_07=256'h71730707_832ba006_10900674_73070773_2a880671_84067281_0771832a_0671872a; +defparam bootram.RAM4.INIT_08=256'h51555255_0c515253_0682c080_077081ff_0778872b_c0067072_76852b80_7081ff06; +defparam bootram.RAM4.INIT_09=256'h983f81aa_81ff51ff_51ff9e3f_075381ff_0681d00a_0d74d00a_0d04fe3d_5552853d; +defparam bootram.RAM4.INIT_0A=256'h5252fef5_7081ff06_3f72882a_e151ff81_ff873f80_8c3fb251_819951ff_51ff923f; +defparam bootram.RAM4.INIT_0B=256'h902a7081_fedb3f72_72982a51_51fee23f_e83f8181_3fb251fe_0651feed_3f7281ff; +defparam bootram.RAM4.INIT_0C=256'ha051feb5_51feba3f_febf3f8e_c43f8051_81a151fe_51feca3f_fecf3fb0_ff065253; +defparam bootram.RAM4.INIT_0D=256'h05538052_54873dfc_fb3d0d82_843d0d04_51fea63f_feab3f80_b03fa051_3f8051fe; +defparam bootram.RAM4.INIT_0E=256'h12085859_d73d0884_d53d0880_b23d0d80_3d0d04ff_22800c87_ce3f863d_80d05183; +defparam bootram.RAM4.INIT_0F=256'h2681b238_16567596_bc39ff9f_dff53f81_80e68451_53829452_26903877_57778293; +defparam bootram.RAM4.INIT_10=256'h0b81e2cc_e18c0c81_5e810b81_3f800808_c15cd4c0_75080480_e6d00556_75842980; +defparam bootram.RAM4.INIT_11=256'hfef63f80_5c80f839_085f80c6_8c9d3f80_3f80085e_8a398c99_e4d00c81_0c800b81; +defparam bootram.RAM4.INIT_12=256'hc55c80d3_89f53f80_80f3c851_8c170852_90170853_5c80e839_065e80d6_0883ffff; +defparam bootram.RAM4.INIT_13=256'h39941753_80c25cb7_c45cbc39_2e863880_06567580_800881ff_518aba3f_3980f3c8; +defparam bootram.RAM4.INIT_14=256'h1708518b_1708528c_80055390_80d03dfe_d75ca439_88dc3f80_8c170851_90170852; +defparam bootram.RAM4.INIT_15=256'h54800b83_3dfdec05_945580d0_39a05c82_fcf83f83_d35c8051_5c8d3980_b93f80d2; +defparam bootram.RAM4.INIT_16=256'he6fe3f80_83808251_7826ec38_81185888_75337734_79055757_1980d23d_3d5a5877; +defparam bootram.RAM4.INIT_17=256'h05ab0533_05330284_3d0d02a7_83973ff9_ce3fff51_e7ac51d4_803d0d80_d03d0d04; +defparam bootram.RAM4.INIT_18=256'h7f7f5a57_3d0d7a7c_3d0d04f8_dfeb3f89_81528051_98547553_57578255_71882b07; +defparam bootram.RAM4.INIT_19=256'h8405a105_05583302_34767081_54738a3d_75811757_7425b738_16565480_575874ff; +defparam bootram.RAM4.INIT_1A=256'h802e8538_d8c73f73_06548a51_800881ff_51d7993f_7781ff06_3dfc0552_3482538a; +defparam bootram.RAM4.INIT_1B=256'h883d3481_38dc5675_de567483_05335580_3d0d02a3_3d0d04fa_73800c8a_c1398154; +defparam bootram.RAM4.INIT_1C=256'h893d3481_02ab0533_3d0d7c57_3d0d04f9_ff893f88_5280d051_055381f7_54883dfc; +defparam bootram.RAM4.INIT_1D=256'h38807725_73802e9e_06705654_800881ff_56d6b93f_05337052_055202a7_53893dfc; +defparam bootram.RAM4.INIT_1E=256'h0c893d0d_81557480_802e8338_70565473_0881ff06_d4fc3f80_7b527551_97387653; +defparam bootram.RAM4.INIT_1F=256'h80de2e09_33565674_800b883d_51ffa03f_f75280d0_fc055381_8154883d_04fa3d0d; +defparam bootram.RAM4.INIT_20=256'h81c0ac0c_800ca60b_eb0b81c0_c0940c80_04990b81_0c883d0d_81567580_81068338; +defparam bootram.RAM4.INIT_21=256'hc0a00c81_51820b81_81c0980c_06708107_882bbe80_803d0d72_c0b00c04_89b00b81; +defparam bootram.RAM4.INIT_22=256'h72882bbe_04803d0d_0c823d0d_c0a80880_70f13881_06515151_812a7081_c0a40870; +defparam bootram.RAM4.INIT_23=256'h70810651_0870812a_0c81c0a4_0b81c0a0_c09c0c84_0c517381_0781c098_80067081; +defparam bootram.RAM4.INIT_24=256'h83065271_71913875_55555757_7c728306_3d0d787a_04ff39fa_38823d0d_515170f1; +defparam bootram.RAM4.INIT_25=256'h822b7711_27943873_55557375_72822a72_5188ca3f_2e863881_06527180_8a387283; +defparam bootram.RAM4.INIT_26=256'h80e7b811_2a708f06_0d747084_0d04fe3d_e939883d_52811454_720c5254_77127008; +defparam bootram.RAM4.INIT_27=256'h82e09008_04803d0d_3f843d0d_5253cffb_e7b81133_728f0680_53d0883f_33545153; +defparam bootram.RAM4.INIT_28=256'h8c800607_80ff067a_93053378_fe3d0d02_823d0d04_5170f138_81065151_70882a70; +defparam bootram.RAM4.INIT_29=256'h82e0900c_e0800c71_f1387682_51515170_2a708106_90087088_535382e0_80c08007; +defparam bootram.RAM4.INIT_2A=256'h882a7081_e0900870_2e963882_72517280_82e0900c_71828007_82e0980c_7581ff06; +defparam bootram.RAM4.INIT_2B=256'h88805588_82e0940c_3d0d810b_3d0d04fc_70800c84_e0800851_70f13882_06515151; +defparam bootram.RAM4.INIT_2C=256'h51fef13f_53815281_90548a80_0d888055_0d04fc3d_873f863d_528051ff_54805380; +defparam bootram.RAM4.INIT_2D=256'h863d0d04_51fed53f_53815280_55885486_3d0d8880_3d0d04fc_06800c86_800881ff; +defparam bootram.RAM4.INIT_2E=256'h70802ef4_81ff0651_eb3f8008_04803d0d_0c823d0d_32810680_3f800881_803d0dca; +defparam bootram.RAM4.INIT_2F=256'h5475fe9b_888055a0_3fffb43f_269b38dd_3f758008_775684e3_04fb3d0d_38823d0d; +defparam bootram.RAM4.INIT_30=256'h08ff1156_0880cb3d_0d80c93d_04ffba3d_3f873d0d_8051fe84_07538152_0a069b0a; +defparam bootram.RAM4.INIT_31=256'hff52883d_82805381_2681a738_3f738008_1754849f_81b43875_7381ff26_57578055; +defparam bootram.RAM4.INIT_32=256'h82e0980c_fd9f3f74_3ffed43f_ea3ffefd_5273518a_80cb3d08_8f3f7553_7052548c; +defparam bootram.RAM4.INIT_33=256'h0b82e090_980c88a0_810b82e0_82e0800c_80c00a07_fec00a06_e0900c76_88800b82; +defparam bootram.RAM4.INIT_34=256'h84157008_8c0c54fe_700882e0_56fe8015_c83d558f_fcef3f80_82e0900c_0c8aa00b; +defparam bootram.RAM4.INIT_35=256'h82e0900c_5488800b_82e0800c_8c157008_840c54fe_700882e0_54fe8815_82e0880c; +defparam bootram.RAM4.INIT_36=256'h81557480_82e0980c_bc38800b_758025ff_90165656_b03fff16_e0900cfc_8a800b82; +defparam bootram.RAM4.INIT_37=256'hcb388157_80082680_3f805773_565682db_7212575a_0d797b7d_0d04f93d_0c80c83d; +defparam bootram.RAM4.INIT_38=256'h76547753_75278338_75555776_82807431_802ea238_ff065473_c3387581_74802e80; +defparam bootram.RAM4.INIT_39=256'he1388280_82807527_8e387454_5674802e_76315759_16741976_fdeb3f73_73527551; +defparam bootram.RAM4.INIT_3A=256'h38731354_73802e8d_7a565455_3d0d7678_3d0d04fc_76800c89_8c3f8157_54dc39fd; +defparam bootram.RAM4.INIT_3B=256'h08307074_81cb3f80_750ca639_84160c80_160c800b_38800b88_08742790_81ed3f80; +defparam bootram.RAM4.INIT_3C=256'h3f863d0d_7151fcc9_7188160c_0684160c_72760c74_3f800830_515281bd_06ff1656; +defparam bootram.RAM4.INIT_3D=256'h15082e94_88140884_9f388153_5271802e_ff067054_3f800881_7554fc98_04fd3d0d; +defparam bootram.RAM4.INIT_3E=256'h3d0d8880_3d0d04fc_72800c85_943f8053_160c51fc_08057088_88140880_3881823f; +defparam bootram.RAM4.INIT_3F=256'h0d80f3a0_0d04ff3d_800c863d_fe800a06_a33f8008_528151fa_f90a5381_55a05481; +defparam bootram.RAM5.INIT_00=256'h387180f3_09810693_5170a02e_ff065451_06800881_2a7081ff_3f800888_08a038d7; +defparam bootram.RAM5.INIT_01=256'h0880e882_04c03f80_0c833d0d_b33f7180_278438f5_52528271_a008ea11_a00c80f3; +defparam bootram.RAM5.INIT_02=256'h0d7d56f9_0c04f63d_80082b80_a93f810b_800c04ff_0b80082b_04f33f81_0533800c; +defparam bootram.RAM5.INIT_03=256'h810b82e0_82e0800c_0c7c882b_0b82e084_e0900c8b_88800b82_82e0980c_983f800b; +defparam bootram.RAM5.INIT_04=256'h3888800b_762780d3_55805473_f8e73f7e_82e0900c_0c8aa80b_0b82e090_980c88a8; +defparam bootram.RAM5.INIT_05=256'h5a82e080_82e08408_e0880859_8c085882_cc3f82e0_e0900cf8_8a800b82_82e0900c; +defparam bootram.RAM5.INIT_06=256'h70337570_38711751_71732791_70538052_73278338_57905370_76753152_085b883d; +defparam bootram.RAM5.INIT_07=256'h7251f789_04803d0d_0c8c3d0d_0b82e098_ffa93980_39721454_811252ec_81055734; +defparam bootram.RAM5.INIT_08=256'h70800c54_de3f8008_05085182_528c0888_088c0508_0d80538c_8c0cfd3d_3f8c0802; +defparam bootram.RAM5.INIT_09=256'h82b93f80_88050851_08528c08_8c088c05_3d0d8153_028c0cfd_0c048c08_853d0d8c; +defparam bootram.RAM5.INIT_0A=256'h88050880_050c8c08_0b8c08fc_f93d0d80_08028c0c_8c0c048c_54853d0d_0870800c; +defparam bootram.RAM5.INIT_0B=256'h810b8c08_05088838_0c8c08fc_8c08f405_050c800b_308c0888_08880508_25ab388c; +defparam bootram.RAM5.INIT_0C=256'h8c088c05_8c050830_ab388c08_05088025_0c8c088c_8c08fc05_08f40508_f4050c8c; +defparam bootram.RAM5.INIT_0D=256'h08fc050c_f005088c_050c8c08_0b8c08f0_08883881_8c08fc05_08f0050c_0c800b8c; +defparam bootram.RAM5.INIT_0E=256'hfc050880_0c548c08_8c08f805_3f800870_085181a7_8c088805_8c050852_80538c08; +defparam bootram.RAM5.INIT_0F=256'h048c0802_3d0d8c0c_800c5489_f8050870_050c8c08_308c08f8_08f80508_2e8c388c; +defparam bootram.RAM5.INIT_10=256'h0888050c_0508308c_388c0888_08802593_8c088805_08fc050c_0d800b8c_8c0cfb3d; +defparam bootram.RAM5.INIT_11=256'h538c088c_8c050c81_08308c08_8c088c05_80258c38_088c0508_fc050c8c_810b8c08; +defparam bootram.RAM5.INIT_12=256'h388c08f8_08802e8c_8c08fc05_f8050c54_08708c08_51ad3f80_08880508_0508528c; +defparam bootram.RAM5.INIT_13=256'hfd3d0d81_08028c0c_8c0c048c_54873d0d_0870800c_8c08f805_08f8050c_0508308c; +defparam bootram.RAM5.INIT_14=256'hfc050880_ac388c08_88050827_05088c08_0c8c088c_8c08f805_050c800b_0b8c08fc; +defparam bootram.RAM5.INIT_15=256'h08108c08_8c08fc05_088c050c_0508108c_388c088c_05082499_0b8c088c_2ea33880; +defparam bootram.RAM5.INIT_16=256'h8c088805_0826a138_8c088805_088c0508_80c9388c_0508802e_398c08fc_fc050cc9; +defparam bootram.RAM5.INIT_17=256'h8c08fc05_08f8050c_0508078c_088c08fc_8c08f805_0888050c_0508318c_088c088c; +defparam bootram.RAM5.INIT_18=256'h802e8f38_08900508_ffaf398c_088c050c_08812a8c_8c088c05_08fc050c_08812a8c; +defparam bootram.RAM5.INIT_19=256'hf4050880_0c518c08_8c08f405_f8050870_8d398c08_f4050c51_08708c08_8c088805; +defparam bootram.RAM5.INIT_1A=256'h802eb038_83065170_38747407_8372278c_79565652_3d0d7877_8c0c04fc_0c853d0d; +defparam bootram.RAM5.INIT_1B=256'h54555571_8115ff14_bd388115_2e098106_52537271_74337433_ff2ea038_ff125271; +defparam bootram.RAM5.INIT_1C=256'h38841184_0981068f_0873082e_74545170_3d0d0474_0b800c86_06e23880_ff2e0981; +defparam bootram.RAM5.INIT_1D=256'h3d0d7670_3d0d04fc_31800c86_af397271_735555ff_26e93870_54517183_14fc1454; +defparam bootram.RAM5.INIT_1E=256'h98387270_5271ff2e_a738ff12_5170802e_75078306_278c3872_55558f72_797b5555; +defparam bootram.RAM5.INIT_1F=256'h74517270_863d0d04_3874800c_098106ea_5271ff2e_5634ff12_74708105_81055433; +defparam bootram.RAM5.INIT_20=256'h71708405_84055408_530c7270_71708405_84055408_530c7270_71708405_84055408; +defparam bootram.RAM5.INIT_21=256'h70840554_27953872_c9388372_52718f26_530cf012_71708405_84055408_530c7270; +defparam bootram.RAM5.INIT_22=256'h059f0533_7971028c_fc3d0d76_54ff8339_26ed3870_12527183_05530cfc_08717084; +defparam bootram.RAM5.INIT_23=256'h70810555_93387373_5271ff2e_a238ff12_5170802e_38748306_8372278a_57555355; +defparam bootram.RAM5.INIT_24=256'h2b075154_07707190_74882b75_3d0d0474_74800c86_8106ef38_71ff2e09_34ff1252; +defparam bootram.RAM5.INIT_25=256'h71708405_05530c72_72717084_8405530c_0c727170_70840553_a5387271_518f7227; +defparam bootram.RAM5.INIT_26=256'hf2387053_52718326_530cfc12_71708405_27903872_dd388372_52718f26_530cf012; +defparam bootram.RAM5.INIT_27=256'h2e80d438_06517080_71740783_2e80d938_55527280_7c705455_3d0d787a_ff9039fa; +defparam bootram.RAM5.INIT_28=256'h7081ff06_2e818738_a9387280_2e098106_56517471_71337433_ff2eb138_ff135372; +defparam bootram.RAM5.INIT_29=256'h56517081_71337433_8106d138_72ff2e09_15555552_128115ff_80fc3881_5170802e; +defparam bootram.RAM5.INIT_2A=256'h71087408_73278838_74575583_3d0d0471_70800c88_31515252_ff067171_ff067581; +defparam bootram.RAM5.INIT_2B=256'h70f88482_fdff1206_7009f7fb_b1387408_5372802e_9739fc13_765552ff_2e883874; +defparam bootram.RAM5.INIT_2C=256'h765552fe_2ed03874_74087608_7327d038_17575583_38841584_5151709a_81800651; +defparam bootram.RAM5.INIT_2D=256'hf3a40cff_9e387380_5472812e_e7e80854_0d800b80_0d04fd3d_800c883d_df39800b; +defparam bootram.RAM5.INIT_2E=256'hffad813f_80f3a40c_f6a33f72_3f800851_51ffb5b8_e89c5281_acba3f80_ad9e3fff; +defparam bootram.RAM5.INIT_2F=256'ha40bfc05_3d0d80e8_00ff39ff_51f6863f_9b3f8008_8151ffb5_80e89c52_ffac9d3f; +defparam bootram.RAM5.INIT_30=256'h0404ffad_38833d0d_098106f1_5270ff2e_12700852_38702dfc_70ff2e91_70085252; +defparam bootram.RAM5.INIT_31=256'h636b6574_6c207061_6e74726f_6e20636f_6f722069_21457272_00000040_ac3f0400; +defparam bootram.RAM5.INIT_32=256'h6c697479_74696269_6f6d7061_65642063_70656374_3a204578_646c6572_2068616e; +defparam bootram.RAM5.INIT_33=256'h6f722069_21457272_25640a00_676f7420_62757420_25642c20_62657220_206e756d; +defparam bootram.RAM5.INIT_34=256'h70656374_3a204578_646c6572_2068616e_636b6574_6c207061_6e74726f_6e20636f; +defparam bootram.RAM5.INIT_35=256'h74202564_7420676f_2c206275_68202564_656e6774_6164206c_61796c6f_65642070; +defparam bootram.RAM5.INIT_36=256'h203d2025_70656564_643a2073_616e6765_6b206368_206c696e_0a657468_0a000000; +defparam bootram.RAM5.INIT_37=256'h720a0000_6f616465_6f6f746c_44502062_31302055_50204e32_0a555352_640a0000; +defparam bootram.RAM5.INIT_38=256'h640a0000_723a2025_756d6265_7479206e_62696c69_70617469_20636f6d_46504741; +defparam bootram.RAM5.INIT_39=256'h723a2025_756d6265_7479206e_62696c69_70617469_20636f6d_77617265_4669726d; +defparam bootram.RAM5.INIT_3A=256'h7061636b_65727920_65636f76_69702072_476f7420_00000000_61646472_640a0000; +defparam bootram.RAM5.INIT_3B=256'h00000826_00000826_00000826_00000826_00000826_00000731_00000000_65743a20; +defparam bootram.RAM5.INIT_3C=256'h00000826_00000826_000007fc_00000826_00000826_00000776_0000078d_00000826; +defparam bootram.RAM5.INIT_3D=256'h000007cf_000007ca_000007c5_0000073e_000007aa_00000826_00000826_00000826; +defparam bootram.RAM5.INIT_3E=256'hffffff00_c0a80a01_c0a80a02_3fff0000_0050c285_000007ea_000007dd_000007d6; +defparam bootram.RAM5.INIT_3F=256'h30313233_2e256400_642e2564_25642e25_45000000_01b200d9_05160364_14580a2c; +defparam bootram.RAM6.INIT_00=256'h5f706b74_73656e64_ffff0000_ffffffff_00000000_43444546_38394142_34353637; +defparam bootram.RAM6.INIT_01=256'h72206275_6e642f6f_656e2061_6f66206c_656e7420_69676e6d_6420616c_3a206261; +defparam bootram.RAM6.INIT_02=256'h74206361_6f206869_65642074_6661696c_6f6e3a20_636f6d6d_6e65745f_66000000; +defparam bootram.RAM6.INIT_03=256'h6172703a_646c655f_0a68616e_00000000_666f7220_696e6720_6c6f6f6b_63686520; +defparam bootram.RAM6.INIT_04=256'h6e736973_696e636f_55445020_0a000000_3d202564_697a6520_72642073_20776569; +defparam bootram.RAM6.INIT_05=256'h53746172_0b0b0b0b_00000000_2025640a_3a202564_67746873_206c656e_74656e74; +defparam bootram.RAM6.INIT_06=256'h6164696e_2e204c6f_6d6f6465_61666520_696e2073_50322b20_20555352_74696e67; +defparam bootram.RAM6.INIT_07=256'h20666f72_6b696e67_43686563_00000000_6172652e_69726d77_66652066_67207361; +defparam bootram.RAM6.INIT_08=256'h2e2e2e00_6d616765_47412069_6e204650_6374696f_726f6475_69642070_2076616c; +defparam bootram.RAM6.INIT_09=256'h666f756e_61676520_4120696d_20465047_74696f6e_6f647563_64207072_56616c69; +defparam bootram.RAM6.INIT_0A=256'h616c6964_4e6f2076_742e0000_20626f6f_6720746f_7074696e_7474656d_642e2041; +defparam bootram.RAM6.INIT_0B=256'h2e0a4661_6f756e64_67652066_20696d61_46504741_696f6e20_64756374_2070726f; +defparam bootram.RAM6.INIT_0C=256'h726d7761_6e206669_6c742d69_20627569_6820746f_726f7567_67207468_6c6c696e; +defparam bootram.RAM6.INIT_0D=256'h6520666f_6d776172_20666972_74696f6e_6f647563_64207072_56616c69_72652e00; +defparam bootram.RAM6.INIT_0E=256'h64696e67_206c6f61_73686564_46696e69_2e2e2e00_64696e67_204c6f61_756e642e; +defparam bootram.RAM6.INIT_0F=256'h65747572_523a2052_4552524f_2e000000_6d616765_6e672069_61727469_2e205374; +defparam bootram.RAM6.INIT_10=256'h6f756c64_73207368_20546869_72616d21_70726f67_61696e20_6f6d206d_6e206672; +defparam bootram.RAM6.INIT_11=256'h64756374_2070726f_616c6964_4e6f2076_6e210000_61707065_65722068_206e6576; +defparam bootram.RAM6.INIT_12=256'h7468726f_696e6720_46616c6c_6e642e20_20666f75_77617265_6669726d_696f6e20; +defparam bootram.RAM6.INIT_13=256'h53706565_2e000000_77617265_6669726d_2d696e20_75696c74_746f2062_75676820; +defparam bootram.RAM6.INIT_14=256'h58000000_57455f54_00000000_4e4f4e45_00000000_2025640a_7420746f_64207365; +defparam bootram.RAM6.INIT_15=256'h20666c6f_726e6574_65746865_43000000_45545249_53594d4d_58000000_57455f52; +defparam bootram.RAM6.INIT_16=256'h2077726f_4144563a_4e45475f_4155544f_5048595f_6c3a2000_6e74726f_7720636f; +defparam bootram.RAM6.INIT_17=256'h00000001_00030003_00000000_780a0000_20307825_20676f74_7825782c_74652030; +defparam bootram.RAM6.INIT_18=256'h68616e64_6b657420_20706163_64617465_6e207570_6f722069_21457272_00030203; +defparam bootram.RAM6.INIT_19=256'h2025642c_6e677468_64206c65_796c6f61_64207061_65637465_20457870_6c65723a; +defparam bootram.RAM6.INIT_1A=256'h00002261_000022b1_000022b1_0000220b_00000000_2025640a_20676f74_20627574; +defparam bootram.RAM6.INIT_1B=256'h000022b1_000022b1_000022b1_000022b1_000022b1_000022b1_0000222a_0000224c; +defparam bootram.RAM6.INIT_1C=256'h000022b1_000022a7_00002290_000022b1_000022b1_000022b1_000022b1_000022b1; +defparam bootram.RAM6.INIT_1D=256'h34353637_30313233_00000000_6f72740a_0a0a6162_0000227d_0000223c_000022b1; +defparam bootram.RAM6.INIT_1E=256'hffff00ff_ff00ffff_00ffffff_65000000_792e6578_64756d6d_43444546_38394142; +defparam bootram.RAM6.INIT_1F=256'hffff0031_05050400_01010100_0000342c_00000000_00000000_00000000_ffffff00; +defparam bootram.RAM6.INIT_20=256'h000033c8_10101200_000032a8_000032a0_00003298_00003290_000b0000_0018000f; +defparam bootram.RAM6.INIT_21=256'h00000000_00000000_00000000_00000000_00000000_ffffffff_00000000_ffffffff; defparam bootram.RAM6.INIT_22=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; defparam bootram.RAM6.INIT_23=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; defparam bootram.RAM6.INIT_24=256'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000; diff --git a/fpga/usrp2/top/N2x0/u2plus_core.v b/fpga/usrp2/top/N2x0/u2plus_core.v index abc32406e..e2539e183 100644 --- a/fpga/usrp2/top/N2x0/u2plus_core.v +++ b/fpga/usrp2/top/N2x0/u2plus_core.v @@ -149,10 +149,10 @@ module u2plus_core ); localparam SR_MISC = 0; // 7 regs - localparam SR_SIMTIMER = 8; // 2 + localparam SR_USER_REGS = 8; // 2 localparam SR_TIME64 = 10; // 6 localparam SR_BUF_POOL = 16; // 4 - localparam SR_USER_REGS = 20; // 2 + localparam SR_SPI_CORE = 20; // 3 localparam SR_RX_FRONT = 24; // 5 localparam SR_RX_CTRL0 = 32; // 9 localparam SR_RX_DSP0 = 48; // 7 @@ -278,9 +278,11 @@ module u2plus_core .sf_dat_o(sf_dat_o),.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_i),.sf_ack_i(sf_ack),.sf_err_i(0),.sf_rty_i(0)); - // Unused Slaves 9, b, c + assign s2_ack = 0; assign s4_ack = 0; - assign s9_ack = 0; assign sb_ack = 0; assign sc_ack = 0; + assign s9_ack = 0; + assign sb_ack = 0; + assign sc_ack = 0; // //////////////////////////////////////////////////////////////////////////////////////// // Reset Controller @@ -373,6 +375,10 @@ module u2plus_core wire wr3_ready_i, wr3_ready_o; wire [35:0] wr0_dat, wr1_dat, wr2_dat, wr3_dat; + wire [35:0] sfc_wr_data, sfc_rd_data; + wire sfc_wr_ready, sfc_rd_ready; + wire sfc_wr_valid, sfc_rd_valid; + wire [35:0] tx_err_data; wire tx_err_src_rdy, tx_err_dst_rdy; @@ -393,21 +399,27 @@ module u2plus_core .dsp0_inp_data(wr1_dat), .dsp0_inp_valid(wr1_ready_i), .dsp0_inp_ready(wr1_ready_o), .dsp1_inp_data(wr3_dat), .dsp1_inp_valid(wr3_ready_i), .dsp1_inp_ready(wr3_ready_o), .eth_inp_data(wr2_dat), .eth_inp_valid(wr2_ready_i), .eth_inp_ready(wr2_ready_o), - .err_inp_data(tx_err_data), .err_inp_ready(tx_err_dst_rdy), .err_inp_valid(tx_err_src_rdy), + .err_inp_data(tx_err_data), .err_inp_valid(tx_err_src_rdy), .err_inp_ready(tx_err_dst_rdy), + .ctl_inp_data(sfc_wr_data), .ctl_inp_valid(sfc_wr_valid), .ctl_inp_ready(sfc_wr_ready), .ser_out_data(rd0_dat), .ser_out_valid(rd0_ready_o), .ser_out_ready(rd0_ready_i), .dsp_out_data(rd1_dat), .dsp_out_valid(rd1_ready_o), .dsp_out_ready(rd1_ready_i), + .ctl_out_data(sfc_rd_data), .ctl_out_valid(sfc_rd_valid), .ctl_out_ready(sfc_rd_ready), .eth_out_data(rd2_dat), .eth_out_valid(rd2_ready_o), .eth_out_ready(rd2_ready_i) ); // ///////////////////////////////////////////////////////////////////////// // SPI -- Slave #2 - spi_top shared_spi - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.wb_adr_i(s2_adr[4:0]),.wb_dat_i(s2_dat_o), - .wb_dat_o(s2_dat_i),.wb_sel_i(s2_sel),.wb_we_i(s2_we),.wb_stb_i(s2_stb), - .wb_cyc_i(s2_cyc),.wb_ack_o(s2_ack),.wb_err_o(),.wb_int_o(spi_int), - .ss_pad_o({sen_adc, sen_tx_db,sen_tx_adc,sen_tx_dac,sen_rx_db,sen_rx_adc,sen_rx_dac,sen_dac,sen_clk}), - .sclk_pad_o(sclk),.mosi_pad_o(mosi),.miso_pad_i(miso) ); + wire [31:0] spi_debug; + wire [31:0] spi_readback; + wire spi_ready; + simple_spi_core #(.BASE(SR_SPI_CORE), .WIDTH(9)) shared_spi( + .clock(dsp_clk), .reset(dsp_rst), + .set_stb(set_stb_dsp), .set_addr(set_addr_dsp), .set_data(set_data_dsp), + .readback(spi_readback), .ready(spi_ready), + .sen({sen_adc, sen_tx_db,sen_tx_adc,sen_tx_dac,sen_rx_db,sen_rx_adc,sen_rx_dac,sen_dac,sen_clk}), + .sclk(sclk), .mosi(mosi), .miso(miso), .debug(spi_debug) + ); // ///////////////////////////////////////////////////////////////////////// // I2C -- Slave #3 @@ -436,18 +448,19 @@ module u2plus_core // Buffer Pool Status -- Slave #5 //compatibility number -> increment when the fpga has been sufficiently altered - localparam compat_num = {16'd9, 16'd1}; //major, minor - wire [31:0] churn = 0; //tweak churn until timing meets! + localparam compat_num = {16'd10, 16'd0}; //major, minor + + wire [31:0] irq_readback = {18'b0, button, spi_ready, clk_status, serdes_link_up, 10'b0}; wb_readback_mux buff_pool_status (.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s5_stb), .wb_adr_i(s5_adr), .wb_dat_o(s5_dat_i), .wb_ack_o(s5_ack), - .word00(churn),.word01(32'b0),.word02(32'b0),.word03(32'b0), - .word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0), - .word08(status),.word09(gpio_readback),.word10(vita_time[63:32]), - .word11(vita_time[31:0]),.word12(compat_num),.word13({18'b0, button, 1'b0, clk_status, serdes_link_up, 10'b0}), - .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]) + .word00(spi_readback),.word01(32'hffff_ffff),.word02(32'hffff_ffff),.word03(32'hffff_ffff), + .word04(32'hffff_ffff),.word05(32'hffff_ffff),.word06(32'hffff_ffff),.word07(32'hffff_ffff), + .word08(status),.word09(32'hffff_ffff),.word10(32'hffff_ffff), + .word11(vita_time[31:0]),.word12(compat_num),.word13(irq_readback), + .word14(32'hffff_ffff),.word15(32'hffff_ffff) ); // ///////////////////////////////////////////////////////////////////////// @@ -477,9 +490,19 @@ module u2plus_core assign s7_dat_i = 32'd0; - settings_bus_crossclock settings_bus_crossclock + wire set_stb_dsp0, set_stb_dsp1; + wire [31:0] set_data_dsp0, set_data_dsp1; + wire [7:0] set_addr_dsp0, set_addr_dsp1; + + //mux settings_bus_crossclock and settings_readback_bus_fifo_ctrl with prio + assign set_stb_dsp = set_stb_dsp0 | set_stb_dsp1; + assign set_addr_dsp = set_stb_dsp1? set_addr_dsp1 : set_addr_dsp0; + assign set_data_dsp = set_stb_dsp1? set_data_dsp1 : set_data_dsp0; + + settings_bus_crossclock #(.FLOW_CTRL(1/*on*/)) settings_bus_crossclock (.clk_i(wb_clk), .rst_i(wb_rst), .set_stb_i(set_stb), .set_addr_i(set_addr), .set_data_i(set_data), - .clk_o(dsp_clk), .rst_o(dsp_rst), .set_stb_o(set_stb_dsp), .set_addr_o(set_addr_dsp), .set_data_o(set_data_dsp)); + .clk_o(dsp_clk), .rst_o(dsp_rst), .set_stb_o(set_stb_dsp0), .set_addr_o(set_addr_dsp0), .set_data_o(set_data_dsp0), + .blocked(set_stb_dsp1)); user_settings #(.BASE(SR_USER_REGS)) user_settings (.clk(dsp_clk),.rst(dsp_rst),.set_stb(set_stb_dsp), @@ -487,6 +510,29 @@ module u2plus_core .set_addr_user(set_addr_user),.set_data_user(set_data_user), .set_stb_user(set_stb_user) ); + // ///////////////////////////////////////////////////////////////////////// + // Settings + Readback Bus -- FIFO controlled + + wire [31:0] sfc_debug; + wire sfc_clear; + settings_fifo_ctrl #(.PROT_DEST(3), .PROT_HDR(1)) sfc + ( + .clock(dsp_clk), .reset(dsp_rst), .clear(sfc_clear), + .vita_time(vita_time), .perfs_ready(spi_ready), + .in_data(sfc_rd_data), .in_valid(sfc_rd_valid), .in_ready(sfc_rd_ready), + .out_data(sfc_wr_data), .out_valid(sfc_wr_valid), .out_ready(sfc_wr_ready), + .strobe(set_stb_dsp1), .addr(set_addr_dsp1), .data(set_data_dsp1), + .word00(spi_readback),.word01(32'hffff_ffff),.word02(32'hffff_ffff),.word03(32'hffff_ffff), + .word04(32'hffff_ffff),.word05(32'hffff_ffff),.word06(32'hffff_ffff),.word07(32'hffff_ffff), + .word08(32'hffff_ffff),.word09(gpio_readback),.word10(vita_time[63:32]), + .word11(vita_time[31:0]),.word12(32'hffff_ffff),.word13(irq_readback), + .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]), + .debug(sfc_debug) + ); + + setting_reg #(.my_addr(SR_BUF_POOL+1/*same as packet dispatcher*/),.width(1)) sr_clear_sfc + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.changed(sfc_clear)); + // Output control lines wire [7:0] clock_outs, serdes_outs, adc_outs; assign {clock_ready, clk_en[1:0], clk_sel[1:0]} = clock_outs[4:0]; diff --git a/fpga/usrp2/top/USRP2/Makefile b/fpga/usrp2/top/USRP2/Makefile index 10610c7dc..94480a811 100644 --- a/fpga/usrp2/top/USRP2/Makefile +++ b/fpga/usrp2/top/USRP2/Makefile @@ -70,7 +70,7 @@ SYNTHESIZE_PROPERTIES = \ "Use Clock Enable" Auto \ "Use Synchronous Reset" Auto \ "Use Synchronous Set" Auto \ -"Verilog Macros" "$(CUSTOM_DEFS)" +"Verilog Macros" "FIFO_CTRL_NO_TIME=1 $(CUSTOM_DEFS)" TRANSLATE_PROPERTIES = \ "Macro Search Path" "$(shell pwd)/../../coregen/" diff --git a/fpga/usrp2/top/USRP2/u2_core.v b/fpga/usrp2/top/USRP2/u2_core.v index 93064254f..d8fe8cf10 100644 --- a/fpga/usrp2/top/USRP2/u2_core.v +++ b/fpga/usrp2/top/USRP2/u2_core.v @@ -154,10 +154,10 @@ module u2_core ); localparam SR_MISC = 0; // 7 regs - localparam SR_SIMTIMER = 8; // 2 + localparam SR_USER_REGS = 8; // 2 localparam SR_TIME64 = 10; // 6 localparam SR_BUF_POOL = 16; // 4 - localparam SR_USER_REGS = 20; // 2 + localparam SR_SPI_CORE = 20; // 3 localparam SR_RX_FRONT = 24; // 5 localparam SR_RX_CTRL0 = 32; // 9 localparam SR_RX_DSP0 = 48; // 7 @@ -284,10 +284,14 @@ module u2_core .sf_dat_o(sf_dat_o),.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_i),.sf_ack_i(sf_ack),.sf_err_i(0),.sf_rty_i(0)); - // Unused Slaves 4, 9 and b-f + assign s2_ack = 0; assign s4_ack = 0; - assign s9_ack = 0; assign sb_ack = 0; assign sc_ack = 0; - assign sd_ack = 0; assign se_ack = 0; assign fc_ack = 0; + assign s9_ack = 0; + assign sb_ack = 0; + assign sc_ack = 0; + assign sd_ack = 0; + assign se_ack = 0; + assign sf_ack = 0; // //////////////////////////////////////////////////////////////////////////////////////// // Reset Controller @@ -379,6 +383,10 @@ module u2_core wire wr3_ready_i, wr3_ready_o; wire [35:0] wr0_dat, wr1_dat, wr2_dat, wr3_dat; + wire [35:0] sfc_wr_data, sfc_rd_data; + wire sfc_wr_ready, sfc_rd_ready; + wire sfc_wr_valid, sfc_rd_valid; + wire [35:0] tx_err_data; wire tx_err_src_rdy, tx_err_dst_rdy; @@ -399,21 +407,27 @@ module u2_core .dsp0_inp_data(wr1_dat), .dsp0_inp_valid(wr1_ready_i), .dsp0_inp_ready(wr1_ready_o), .dsp1_inp_data(wr3_dat), .dsp1_inp_valid(wr3_ready_i), .dsp1_inp_ready(wr3_ready_o), .eth_inp_data(wr2_dat), .eth_inp_valid(wr2_ready_i), .eth_inp_ready(wr2_ready_o), - .err_inp_data(tx_err_data), .err_inp_ready(tx_err_dst_rdy), .err_inp_valid(tx_err_src_rdy), + .err_inp_data(tx_err_data), .err_inp_valid(tx_err_src_rdy), .err_inp_ready(tx_err_dst_rdy), + .ctl_inp_data(sfc_wr_data), .ctl_inp_valid(sfc_wr_valid), .ctl_inp_ready(sfc_wr_ready), .ser_out_data(rd0_dat), .ser_out_valid(rd0_ready_o), .ser_out_ready(rd0_ready_i), .dsp_out_data(rd1_dat), .dsp_out_valid(rd1_ready_o), .dsp_out_ready(rd1_ready_i), + .ctl_out_data(sfc_rd_data), .ctl_out_valid(sfc_rd_valid), .ctl_out_ready(sfc_rd_ready), .eth_out_data(rd2_dat), .eth_out_valid(rd2_ready_o), .eth_out_ready(rd2_ready_i) ); // ///////////////////////////////////////////////////////////////////////// // SPI -- Slave #2 - spi_top shared_spi - (.wb_clk_i(wb_clk),.wb_rst_i(wb_rst),.wb_adr_i(s2_adr[4:0]),.wb_dat_i(s2_dat_o), - .wb_dat_o(s2_dat_i),.wb_sel_i(s2_sel),.wb_we_i(s2_we),.wb_stb_i(s2_stb), - .wb_cyc_i(s2_cyc),.wb_ack_o(s2_ack),.wb_err_o(),.wb_int_o(spi_int), - .ss_pad_o({sen_tx_db,sen_tx_adc,sen_tx_dac,sen_rx_db,sen_rx_adc,sen_rx_dac,sen_dac,sen_clk}), - .sclk_pad_o(sclk),.mosi_pad_o(mosi),.miso_pad_i(miso) ); + wire [31:0] spi_debug; + wire [31:0] spi_readback; + wire spi_ready; + simple_spi_core #(.BASE(SR_SPI_CORE), .WIDTH(8)) shared_spi( + .clock(dsp_clk), .reset(dsp_rst), + .set_stb(set_stb_dsp), .set_addr(set_addr_dsp), .set_data(set_data_dsp), + .readback(spi_readback), .ready(spi_ready), + .sen({sen_tx_db,sen_tx_adc,sen_tx_dac,sen_rx_db,sen_rx_adc,sen_rx_dac,sen_dac,sen_clk}), + .sclk(sclk), .mosi(mosi), .miso(miso), .debug(spi_debug) + ); // ///////////////////////////////////////////////////////////////////////// // I2C -- Slave #3 @@ -442,17 +456,18 @@ module u2_core // Buffer Pool Status -- Slave #5 //compatibility number -> increment when the fpga has been sufficiently altered - localparam compat_num = {16'd9, 16'd1}; //major, minor - wire [31:0] churn = 0; //tweak churn until timing meets! + localparam compat_num = {16'd10, 16'd0}; //major, minor + + wire [31:0] irq_readback = {19'b0, spi_ready, clk_status, serdes_link_up, 10'b0}; wb_readback_mux buff_pool_status (.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s5_stb), .wb_adr_i(s5_adr), .wb_dat_o(s5_dat_i), .wb_ack_o(s5_ack), - .word00(churn),.word01(32'b0),.word02(32'b0),.word03(32'b0), - .word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0), + .word00(spi_readback),.word01(32'hffff_ffff),.word02(32'hffff_ffff),.word03(32'hffff_ffff), + .word04(32'hffff_ffff),.word05(32'hffff_ffff),.word06(32'hffff_ffff),.word07(32'hffff_ffff), .word08(status),.word09(gpio_readback),.word10(vita_time[63:32]), - .word11(vita_time[31:0]),.word12(compat_num),.word13({20'b0, clk_status, serdes_link_up, 10'b0}), + .word11(vita_time[31:0]),.word12(compat_num),.word13(irq_readback), .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]) ); @@ -483,9 +498,19 @@ module u2_core assign s7_dat_i = 32'd0; - settings_bus_crossclock settings_bus_crossclock + wire set_stb_dsp0, set_stb_dsp1; + wire [31:0] set_data_dsp0, set_data_dsp1; + wire [7:0] set_addr_dsp0, set_addr_dsp1; + + //mux settings_bus_crossclock and settings_readback_bus_fifo_ctrl with prio + assign set_stb_dsp = set_stb_dsp0 | set_stb_dsp1; + assign set_addr_dsp = set_stb_dsp1? set_addr_dsp1 : set_addr_dsp0; + assign set_data_dsp = set_stb_dsp1? set_data_dsp1 : set_data_dsp0; + + settings_bus_crossclock #(.FLOW_CTRL(1/*on*/)) settings_bus_crossclock (.clk_i(wb_clk), .rst_i(wb_rst), .set_stb_i(set_stb), .set_addr_i(set_addr), .set_data_i(set_data), - .clk_o(dsp_clk), .rst_o(dsp_rst), .set_stb_o(set_stb_dsp), .set_addr_o(set_addr_dsp), .set_data_o(set_data_dsp)); + .clk_o(dsp_clk), .rst_o(dsp_rst), .set_stb_o(set_stb_dsp0), .set_addr_o(set_addr_dsp0), .set_data_o(set_data_dsp0), + .blocked(set_stb_dsp1)); user_settings #(.BASE(SR_USER_REGS)) user_settings (.clk(dsp_clk),.rst(dsp_rst),.set_stb(set_stb_dsp), @@ -493,6 +518,38 @@ module u2_core .set_addr_user(set_addr_user),.set_data_user(set_data_user), .set_stb_user(set_stb_user) ); + // ///////////////////////////////////////////////////////////////////////// + // Settings + Readback Bus -- FIFO controlled + + wire [31:0] sfc_debug; + wire sfc_clear; + /* + settings_fifo_ctrl #(.PROT_DEST(3), .PROT_HDR(1)) sfc + ( + .clock(dsp_clk), .reset(dsp_rst), .clear(sfc_clear), + .vita_time(vita_time), .perfs_ready(spi_ready), + .in_data(sfc_rd_data), .in_valid(sfc_rd_valid), .in_ready(sfc_rd_ready), + .out_data(sfc_wr_data), .out_valid(sfc_wr_valid), .out_ready(sfc_wr_ready), + .strobe(set_stb_dsp1), .addr(set_addr_dsp1), .data(set_data_dsp1), + .word00(spi_readback),.word01(32'hffff_ffff),.word02(32'hffff_ffff),.word03(32'hffff_ffff), + .word04(32'hffff_ffff),.word05(32'hffff_ffff),.word06(32'hffff_ffff),.word07(32'hffff_ffff), + .word08(32'hffff_ffff),.word09(gpio_readback),.word10(vita_time[63:32]), + .word11(vita_time[31:0]),.word12(32'hffff_ffff),.word13(irq_readback), + .word14(vita_time_pps[63:32]),.word15(vita_time_pps[31:0]), + .debug(sfc_debug) + ); + */ + assign sfc_debug = 0; + assign set_stb_dsp1 = 0; + assign set_addr_dsp1 = 0; + assign set_data_dsp1 = 0; + assign sfc_rd_ready = 1; + assign sfc_wr_valid = 0; + assign sfc_wr_data = 0; + + setting_reg #(.my_addr(SR_BUF_POOL+1/*same as packet dispatcher*/),.width(1)) sr_clear_sfc + (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.changed(sfc_clear)); + // Output control lines wire [7:0] clock_outs, serdes_outs, adc_outs; assign {clock_ready, clk_en[1:0], clk_sel[1:0]} = clock_outs[4:0]; diff --git a/fpga/usrp2/top/extract_usage.py b/fpga/usrp2/top/extract_usage.py new file mode 100755 index 000000000..55fbf384c --- /dev/null +++ b/fpga/usrp2/top/extract_usage.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Copyright 2012 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/>. +# + +import os +import sys + +ALL_MAP_FILES = """\ +./N2x0/build-N210R4/u2plus_map.map N210 +./N2x0/build-N200R4/u2plus_map.map N200 +./USRP2/build/u2_rev3_map.map USRP2 +./E1x0/build-E100/u1e_map.map E100 +./E1x0/build-E110/u1e_map.map E110 +./B100/build-B100/B100_map.map B100 +""" + +def extract_map_from_file(path): + output = '' + found = False + for line in open(path).readlines(): + if line.strip() == 'Mapping completed.': found = False + if line.strip() == 'Logic Utilization:': found = True + if found: output += line + return output + +def extract_maps(): + output = '' + for line in ALL_MAP_FILES.splitlines(): + path, name = line.split() + if not os.path.exists(path): + print 'DNE ', path, ' skipping...' + output += """ + + + +######################################################################## +## %s Usage Summary +######################################################################## + +%s"""%(name, extract_map_from_file(path).strip()) + return output + '\n\n' + +if __name__ == '__main__': + summary = extract_maps() + if len(sys.argv) == 1: print summary + else: open(sys.argv[1], 'w').write(summary) diff --git a/fpga/usrp2/vrt/vita_rx_framer.v b/fpga/usrp2/vrt/vita_rx_framer.v index 514df1151..6e4b8025d 100644 --- a/fpga/usrp2/vrt/vita_rx_framer.v +++ b/fpga/usrp2/vrt/vita_rx_framer.v @@ -85,9 +85,11 @@ module vita_rx_framer (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),.out(samples_per_packet),.changed()); - setting_reg #(.my_addr(BASE+8),.width(4), .at_reset(1)) sr_numchan + assign numchan = 0;/* + setting_reg #(.my_addr(BASE+8),.width(4), .at_reset(0)) sr_numchan (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),.out(numchan),.changed()); + */ // Output FIFO for packetized data localparam VITA_IDLE = 0; @@ -164,7 +166,7 @@ module vita_rx_framer VITA_PAYLOAD : if(sample_fifo_src_rdy_i) begin - if(sample_phase == (numchan-4'd1)) + if(sample_phase == numchan) begin sample_phase <= 0; sample_ctr <= sample_ctr + 1; @@ -213,7 +215,7 @@ module vita_rx_framer assign data_o[35:34] = 2'b00; // Always write full lines assign sample_fifo_dst_rdy_o = pkt_fifo_rdy & ( ((vita_state==VITA_PAYLOAD) & - (sample_phase == (numchan-4'd1)) & + (sample_phase == numchan) & ~|flags_fifo_o[4:1]) | (vita_state==VITA_ERR_PAYLOAD)); diff --git a/fpga/usrp2/vrt/vita_tx_deframer.v b/fpga/usrp2/vrt/vita_tx_deframer.v index 6919da11a..ed3916311 100644 --- a/fpga/usrp2/vrt/vita_tx_deframer.v +++ b/fpga/usrp2/vrt/vita_tx_deframer.v @@ -43,10 +43,11 @@ module vita_tx_deframer localparam FIFOWIDTH = 5+64+16+(32*MAXCHAN); - wire [1:0] numchan; + wire [1:0] numchan = 0;/* setting_reg #(.my_addr(BASE), .at_reset(0), .width(2)) sr_numchan (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),.out(numchan),.changed()); + */ reg [3:0] vita_state; wire has_streamid, has_classid, has_secs, has_tics, has_trailer; diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 55fd5630f..e3f08c03a 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -195,6 +195,12 @@ INSTALL(FILES ) ######################################################################## +# Images download directory for utils/uhd_images_downloader.py +######################################################################## + +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/master_images/archive/uhd-images_003.004.003-205-g4896bf78.zip") + +######################################################################## # Register top level components ######################################################################## LIBUHD_REGISTER_COMPONENT("LibUHD" ENABLE_LIBUHD ON "Boost_FOUND;HAVE_PYTHON_PLAT_MIN_VERSION;HAVE_PYTHON_MODULE_CHEETAH" OFF) @@ -225,8 +231,6 @@ IF(ENABLE_UTILS) ADD_SUBDIRECTORY(utils) ENDIF(ENABLE_UTILS) -ADD_SUBDIRECTORY(usrp_e_utils) - ######################################################################## # Create Pkg Config File ######################################################################## @@ -278,3 +282,4 @@ ENDIF(DEFINED UHD_IMAGES_DIR AND EXISTS "${UHD_IMAGES_DIR}") UHD_PRINT_COMPONENT_SUMMARY() MESSAGE(STATUS "Building version: ${UHD_VERSION}") MESSAGE(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") +MESSAGE(STATUS "Compatible images can be downloaded from: ${UHD_IMAGES_DOWNLOAD_SRC}") diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index e393a79f0..f56358ca9 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2012 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 @@ -33,8 +33,8 @@ SET(manual_sources transport.rst usrp1.rst usrp2.rst - usrp_b1xx.rst - usrp_e1xx.rst + usrp_b100.rst + usrp_e1x0.rst ) ######################################################################## diff --git a/host/docs/build.rst b/host/docs/build.rst index ee522780a..0a18e9e9a 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -14,25 +14,25 @@ the dependencies should be available in the package repositories for your package manager. **Mac OS X Notes:** -Install the "Xcode Developer Tools" to get the build tools (gcc and make). +Install the "Xcode Developer Tools" to get the build tools (GCC and Make). Use MacPorts to get the Boost and Cheetah dependencies. -Other dependencies can be downloaded as dmg installers from the web. +Other dependencies can be downloaded as DMG installers from the web. **Windows Notes:** -The dependencies can be acquired through installable exe files. -Usually, the windows installer can be found on the project's website. -Some projects do not host windows installers, and if this is the case, -follow the auxiliary download url for the windows installer (below). +The dependencies can be acquired through installable EXE files. +Usually, the Windows installer can be found on the project's website. +Some projects do not host Windows installers, and if this is the case, +follow the auxiliary download URL for the Windows installer (below). ^^^^^^^^^^^^^^^^ Git ^^^^^^^^^^^^^^^^ Required to check out the repository. -On windows, install cygwin with git support to checkout the repository, -or install msysgit from http://code.google.com/p/msysgit/downloads/list +On Windows, install Cygwin with Git support to checkout the repository +or install msysGit from http://code.google.com/p/msysgit/downloads/list. ^^^^^^^^^^^^^^^^ -C++ compiler +C++ Compiler ^^^^^^^^^^^^^^^^ The following compilers are known to work: @@ -44,7 +44,7 @@ The following compilers are known to work: CMake ^^^^^^^^^^^^^^^^ * **Purpose:** generates project build files -* **Version:** at least 2.6 +* **Minimum Version:** 2.6 * **Usage:** build time (required) * **Download URL:** http://www.cmake.org/cmake/resources/software.html @@ -52,57 +52,57 @@ CMake Boost ^^^^^^^^^^^^^^^^ * **Purpose:** C++ library -* **Version:** at least 1.36 unix, at least 1.40 windows -* **Usage:** build time + run time (required) +* **Minimum Version:** 1.36 (Linux), 1.40 (Windows) +* **Usage:** build time + runtime (required) * **Download URL:** http://www.boost.org/users/download/ -* **Download URL (windows installer):** http://www.boostpro.com/download +* **Download URL (Windows installer):** http://www.boostpro.com/download ^^^^^^^^^^^^^^^^ LibUSB ^^^^^^^^^^^^^^^^ * **Purpose:** USB-based hardware support -* **Version:** at least 1.0 -* **Usage:** build time + run time (optional) +* **Minimum Version:** 1.0 +* **Usage:** build time + runtime (optional) * **Download URL:** http://sourceforge.net/projects/libusb/files/libusb-1.0/ -* **Download URL (windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots +* **Download URL (Windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots ^^^^^^^^^^^^^^^^ Python ^^^^^^^^^^^^^^^^ * **Purpose:** used by Cheetah and utility scripts -* **Version:** at least 2.6 -* **Usage:** build time + run time utility scripts (required) +* **Minimum Version:** 2.6 +* **Usage:** build time + runtime utility scripts (required) * **Download URL:** http://www.python.org/download/ ^^^^^^^^^^^^^^^^ Cheetah ^^^^^^^^^^^^^^^^ * **Purpose:** source code generation -* **Version:** at least 2.0 +* **Minimum Version:** 2.0 * **Usage:** build time (required) * **Download URL:** http://www.cheetahtemplate.org/download.html -* **Download URL (windows installer):** http://feisley.com/python/cheetah/ +* **Download URL (Windows installer):** http://feisley.com/python/cheetah/ **Alternative method:** -Install setuptools, and use the easy_install command to install Cheetah. +Install **setuptools**, and use the **easy_install** command to install Cheetah. http://pypi.python.org/pypi/setuptools ^^^^^^^^^^^^^^^^ Doxygen ^^^^^^^^^^^^^^^^ -* **Purpose:** generates html api documentation +* **Purpose:** generates HTML API documentation * **Usage:** build time (optional) * **Download URL:** http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc ^^^^^^^^^^^^^^^^ Docutils ^^^^^^^^^^^^^^^^ -* **Purpose:** generates html user manual +* **Purpose:** generates HTML user manual * **Usage:** build time (optional) * **Download URL:** http://docutils.sourceforge.net/ **Alternate method:** -Install setuptools, and use the easy_install command to install Docutils. +Install **setuptools**, and use the **easy_install** command to install Docutils. http://pypi.python.org/pypi/setuptools ------------------------------------------------------------------------ @@ -110,7 +110,7 @@ Build Instructions (Unix) ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Generate Makefiles with cmake +Generate Makefiles with CMake ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -119,11 +119,11 @@ Generate Makefiles with cmake cd build cmake ../ -Additionally, configuration variables can be passed into cmake via the command line. +Additionally, configuration variables can be passed into CMake via the command line. The following common-use configuration variables are listed below: -* For a custom install prefix: -DCMAKE_INSTALL_PREFIX=<install-path> -* To install libs into lib64: cmake -DLIB_SUFFIX=64 +* For a custom install prefix: **-DCMAKE_INSTALL_PREFIX=<install-path>** +* To install libs into lib64: **cmake -DLIB_SUFFIX=64** Example usage: :: @@ -142,8 +142,8 @@ Build and install ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the library path (Linux) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Make sure that libuhd.so is in your LD_LIBRARY_PATH -or add it to /etc/ld.so.conf and make sure to run: +Make sure that **libuhd.so** is in your **LD_LIBRARY_PATH**, +or add it to **/etc/ld.so.conf** and make sure to run: :: sudo ldconfig @@ -151,46 +151,46 @@ or add it to /etc/ld.so.conf and make sure to run: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the library path (Mac OS X) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Make sure that libuhd.dylib is in your DYLD_LIBRARY_PATH +Make sure that **libuhd.dylib** is in your **DYLD_LIBRARY_PATH**. ------------------------------------------------------------------------ Build Instructions (Windows) ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Generate the project with cmake +Generate the project with CMake ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Open the cmake gui program. -* Set the path to the source code: <uhd-repo-path>/host -* Set the path to the build directory: <uhd-repo-path>/host/build +* Open the CMake GUI. +* Set the path to the source code: **<uhd-repo-path>/host**. +* Set the path to the build directory: **<uhd-repo-path>/host/build**. * Make sure that the paths do not contain spaces. -* Click configure and select the MSVC compiler. -* Set the build variables and click configure again. -* Click generate and a project file will be created in the build directory. +* Click "Configure" and select "Microsoft Visual Studio 10". +* Set the build variables and click "Configure" again. +* Click "Generate", and a project file will be created in the build directory. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LibUSB cmake notes +LibUSB CMake notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Windows, cmake does not have the advantage of pkg-config, -so we must manually tell cmake how to locate the LibUSB header and lib. +On Windows, CMake does not have the advantage of **pkg-config**, +so we must manually tell CMake how to locate the LibUSB header and lib. -* From the cmake gui, select "Advanded View" -* Set LIBUSB_INCLUDE_DIRS to the directory with "libusb.h". -* Set LIBUSB_LIBRARIES to the full path for "libusb-1.0.lib". +* From the CMake GUI, select "Advanced View". +* Set **LIBUSB_INCLUDE_DIRS** to the directory with **libusb.h**. +* Set **LIBUSB_LIBRARIES** to the full path for **libusb-1.0.lib**. - * Recommend the static libusb-1.0.lib to simplify runtime dependencies. + * Recommend the static **libusb-1.0.lib** to simplify runtime dependencies. -* Check the box to enable USB support, click configure and generate. +* Check the box to enable USB support, click "Configure" and "Generate". ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Build the project in MSVC ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Open the generated project file in MSVC. * Change the build type from "Debug" to "Release". -* Select the build all target, right click, and choose build. -* Select the install target, right click, and choose build. +* Select the "Build All" target, right-click, and choose "Build". +* Select the install target, right-click, and choose "Build". -**Note:** you may not have permission to build the install target. +**Note:** You may not have permission to build the install target. You need to be an administrator or to run MSVC as administrator. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,11 +206,11 @@ Open the Visual Studio Command Prompt Shorcut: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the PATH environment variable ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Add the uhd bin path to %PATH% (usually c:\\program files\\uhd\\bin) +* Add the UHD bin path to **%PATH%** (usually **C:\\Program Files\\UHD\\bin**) **Note:** -The interface for editing environment variable paths in Windows is very poor. -I recommend using "Rapid Environment Editor" (http://www.rapidee.com) over the default editor. +The default interface for editing environment variable paths in Windows is very poor. +We recommend using "Rapid Environment Editor" (http://www.rapidee.com) over the default editor. ------------------------------------------------------------------------ Post-Install Tasks diff --git a/host/docs/calibration.rst b/host/docs/calibration.rst index 14e66a312..c97eebfd5 100644 --- a/host/docs/calibration.rst +++ b/host/docs/calibration.rst @@ -7,10 +7,10 @@ UHD - Calibration Application Notes ------------------------------------------------------------------------ Self-calibration ------------------------------------------------------------------------ -The UHD comes with several self-calibration utilities for minimizing IQ imbalance and DC offset. +UHD comes with several self-calibration utilities for minimizing IQ imbalance and DC offset. These utilities perform calibration sweeps using transmit leakage into the receive path (special equipment is not required). -The results from a calibration are written to a csv file in the user's home directory. +The results from a calibration are written to a CSV file in the user's home directory. UHD will automatically apply corrections at runtime when the user re-tunes the daughterboard LO. Calibration results are specific to an individual RF board. @@ -21,21 +21,20 @@ the user should re-apply the desired setting every time the LO is re-tuned. UHD comes with the following calibration utilities: - * **uhd_cal_rx_iq_balance:** - mimimizes RX IQ imbalance vs LO frequency - * **uhd_cal_tx_dc_offset:** - mimimizes TX DC offset vs LO frequency - * **uhd_cal_tx_iq_balance:** - mimimizes TX IQ imbalance vs LO frequency - + * **uhd_cal_rx_iq_balance:** - mimimizes RX IQ imbalance vs. LO frequency + * **uhd_cal_tx_dc_offset:** - mimimizes TX DC offset vs. LO frequency + * **uhd_cal_tx_iq_balance:** - mimimizes TX IQ imbalance vs. LO frequency The following RF frontends are supported by the self-calibration utilities: * WBX transceiver board * SBX transceiver board - * more to come... + * RFX transceiver board ******************************************** -Calibration utilities +Calibration Utilities ******************************************** -UHD installs the calibration utilities into <install-path>/bin. +UHD installs the calibration utilities into **<install-path>/bin**. **Disconnect** any extrernal hardware from the RF antenna ports, and run the following from the command line. Each utility will take several minutes to complete. @@ -49,13 +48,13 @@ See the output given by --help for more advanced options, such as: manually choosing the frequency range and step size for the sweeps. ******************************************** -Calibration data +Calibration Data ******************************************** Calibration files are stored in the user's home/application directory. They can easily be moved from machine to another by copying the "cal" directory. Re-running a calibration utility will replace the existing calibration file. The old calibration file will be renamed so it may be recovered by the user. - * **Unix:** ${HOME}/.uhd/cal/ + * **Linux:** ${HOME}/.uhd/cal/ * **Windows:** %APPDATA%\\.uhd\\cal\\ diff --git a/host/docs/coding.rst b/host/docs/coding.rst index ed858ceb4..ef8cb5fe2 100644 --- a/host/docs/coding.rst +++ b/host/docs/coding.rst @@ -23,7 +23,7 @@ considered to be a "device". The device API provides ways to: See the documentation in *device.hpp* for reference. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -High-Level: The multi usrp +High-Level: The Multi-USRP ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Multi-USRP class provides a fat interface to a single USRP with one or more channels, or multiple USRPs in a homogeneous setup. diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index d41bf824c..29812592f 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -13,7 +13,7 @@ Eventually, this page will be expanded to list out the full properties of each board as well. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Basic RX and and LFRX +Basic RX and LFRX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Basic RX and LFRX boards have 4 frontends: @@ -23,21 +23,21 @@ The Basic RX and LFRX boards have 4 frontends: * **Frontend BA:** quadrature frontend using both antennas (QI) The boards have no tunable elements or programmable gains. -Though the magic of aliasing, you can down-convert signals +Through the magic of aliasing, you can down-convert signals greater than the Nyquist rate of the ADC. BasicRX Bandwidth (Hz): -* For Real-Mode (A or B frontend): 250M -* For Complex (AB or BA frontend): 500M +* **For Real-Mode (A or B frontend)**: 250M +* **For Complex (AB or BA frontend)**: 500M LFRX Bandwidth (Hz): -* For Real-Mode (A or B frontend): 33M -* For Complex (AB or BA frontend): 66M +* **For Real-Mode (A or B frontend)**: 33M +* **For Complex (AB or BA frontend)**: 66M ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Basic TX and and LFTX +Basic TX and LFTX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Basic TX and LFTX boards have 4 frontends: @@ -47,30 +47,30 @@ The Basic TX and LFTX boards have 4 frontends: * **Frontend BA:** quadrature frontend using both antennas (QI) The boards have no tunable elements or programmable gains. -Though the magic of aliasing, you can up-convert signals +Through the magic of aliasing, you can up-convert signals greater than the Nyquist rate of the DAC. BasicTX Bandwidth (Hz): 250M -* For Real-Mode (A or B frontend): 250M -* For Complex (AB or BA frontend): 500M +* **For Real-Mode (A or B frontend**): 250M +* **For Complex (AB or BA frontend)**: 500M LFTX Bandwidth (Hz): 33M -* For Real-Mode (A or B frontend): 33M -* For Complex (AB or BA frontend): 66M +* **For Real-Mode (A or B frontend)**: 33M +* **For Complex (AB or BA frontend)**: 66M ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DBSRX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The DBSRX board has 1 quadrature frontend. -It defaults to direct conversion, but can use a low IF through lo_offset in uhd::tune_request_t +It defaults to direct conversion but can use a low IF through lo_offset in **uhd::tune_request_t**. Receive Antennas: **J3** * **Frontend 0:** Complex baseband signal from antenna J3 -The board has no user selectable antenna setting +The board has no user selectable antenna setting. Receive Gains: @@ -87,13 +87,13 @@ Sensors: DBSRX2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The DBSRX2 board has 1 quadrature frontend. -It defaults to direct conversion, but can use a low IF through lo_offset in uhd::tune_request_t +It defaults to direct conversion, but can use a low IF through lo_offset in **uhd::tune_request_t**. Receive Antennas: **J3** * **Frontend 0:** Complex baseband signal from antenna J3 -The board has no user selectable antenna setting +The board has no user-selectable antenna setting. Receive Gains: @@ -109,9 +109,9 @@ Sensors: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RFX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The RFX Series boards have 2 quadrature frontends, one transmit, one receive. -Transmit defaults to low IF and Receive defaults to direct conversion. -The IF can be adjusted through lo_offset in uhd::tune_request_t +The RFX Series boards have 2 quadrature frontends: Transmit and Receive. +Transmit defaults to low IF, and Receive defaults to direct conversion. +The IF can be adjusted through lo_offset in **uhd::tune_request_t**. The RFX Series boards have independent receive and transmit LO's and synthesizers allowing full-duplex operation on different transmit and receive frequencies. @@ -185,7 +185,7 @@ WBX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The WBX Series boards have 2 quadrature frontends, one transmit, one receive. Transmit and Receive default to direct conversion but -can be used in low IF mode through lo_offset in uhd::tune_request_t +can be used in low IF mode through lo_offset in **uhd::tune_request_t**. The WBX Series boards have independent receive and transmit LO's and synthesizers allowing full-duplex operation on different transmit and receive frequencies. @@ -218,7 +218,7 @@ SBX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The SBX Series boards have 2 quadrature frontends, one transmit, one receive. Transmit and Receive default to direct conversion but -can be used in low IF mode through lo_offset in uhd::tune_request_t +can be used in low IF mode through lo_offset in **uhd::tune_request_t**. The SBX Series boards have independent receive and transmit LO's and synthesizers allowing full-duplex operation on different transmit and receive frequencies. @@ -282,7 +282,7 @@ Receive Frontends: * **Frontend RX1:** real-mode baseband from antenna J100 * **Frontend RX2:** real-mode baseband from antenna J140 -Note: The TVRX2 has always-on AGC, the software controllable gain is the +Note: The TVRX2 has always-on AGC; the software controllable gain is the final gain stage which controls the AGC set-point for output to ADC. Receive Gains: @@ -303,8 +303,8 @@ Daughterboard Modifications Sometimes, daughterboards will require modification to work on certain frequencies or to work with certain hardware. -Modification usually involves moving/removing a SMT component -and burning a new daughterboard id into the eeprom. +Modification usually involves moving/removing an SMT component +and burning a new daughterboard ID into the EEPROM. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DBSRX - Mod @@ -319,13 +319,13 @@ over the standard daughterboard clock lines. **Step 1: Move the clock configuration resistor** -Remove R193 (which is 10 ohms, 0603 size) and put it on R194, which is empty. +Remove **R193** (which is 10 ohms, 0603 size), and put it on **R194**, which is empty. This is made somewhat more complicated by the fact that the silkscreen is not clear in that area. -R193 is on the back, immediately below the large beige connector, J2. -R194 is just below, and to the left of R193. -The silkscreen for R193 is ok, but for R194, +**R193** is on the back, immediately below the large beige connector, **J2**. +**R194** is just below, and to the left of **R193**. +The silkscreen for **R193** is ok, but for **R194**, it is upside down, and partially cut off. -If you lose R193, you can use anything from 0 to 10 ohms there. +If you lose **R193**, you can use anything from 0 to 10 ohms there. **Step 2: Burn a new daughterboard id into the EEPROM** @@ -335,8 +335,8 @@ With the daughterboard plugged-in, run the following commands: cd <install-path>/share/uhd/utils ./usrp_burn_db_eeprom --id=0x000d --unit=RX --args=<args> --slot=<slot> -* <args> are device address arguments (optional if only one USRP is on your machine) -* <slot> is the name of the daughterboard slot (optional if the USRP has only one slot) +* **<args>** are device address arguments (optional if only one USRP is on your machine) +* **<slot>** is the name of the daughterboard slot (optional if the USRP has only one slot) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RFX - Mod @@ -347,14 +347,14 @@ Please follow the modification procedures below: **Step 1: Disable the daughterboard clocks** -Move R64 to R84, Move R142 to R153 +Move **R64** to **R84**. Move **R142** to **R153**. **Step 2: Connect the motherboard blocks** -Move R35 to R36, Move R117 to R115 -These are all 0-ohm, so if you lose one, just short across the appropriate pads +Move **R35** to **R36**. Move **R117** to **R115**. +These are all 0-ohm, so if you lose one, just short across the appropriate pads. -**Step 3: Burn the appropriate daughterboard id into the EEPROM** +**Step 3: Burn the appropriate daughterboard ID into the EEPROM** With the daughterboard plugged-in, run the following commands: :: @@ -363,19 +363,19 @@ With the daughterboard plugged-in, run the following commands: ./usrp_burn_db_eeprom --id=<rx_id> --unit=RX --args=<args> --slot=<slot> ./usrp_burn_db_eeprom --id=<tx_id> --unit=TX --args=<args> --slot=<slot> -* <rx_id> choose the appropriate RX ID for your daughterboard +* **<rx_id>** choose the appropriate RX ID for your daughterboard * **RFX400:** 0x0024 * **RFX900:** 0x0025 * **RFX1800:** 0x0034 * **RFX1200:** 0x0026 * **RFX2400:** 0x0027 -* <tx_id> choose the appropriate TX ID for your daughterboard +* **<tx_id>** choose the appropriate TX ID for your daughterboard * **RFX400:** 0x0028 * **RFX900:** 0x0029 * **RFX1800:** 0x0035 * **RFX1200:** 0x002a * **RFX2400:** 0x002b -* <args> are device address arguments (optional if only one USRP is on your machine) -* <slot> is the name of the daughterboard slot (optional if the USRP has only one slot) +* **<args>** are device address arguments (optional if only one USRP is on your machine) +* **<slot>** is the name of the daughterboard slot (optional if the USRP has only one slot) diff --git a/host/docs/general.rst b/host/docs/general.rst index 5df89fc19..fc7caff3c 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -5,7 +5,7 @@ UHD - General Application Notes .. contents:: Table of Contents ------------------------------------------------------------------------ -Tuning notes +Tuning Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,12 +20,12 @@ In a typical use-case, the user specifies an overall center frequency for the signal chain. The RF front-end will be tuned as close as possible to the center frequency, and the DSP will account for the error in tuning between target frequency and actual frequency. The user may also explicitly control both -stages of tuning through through the tune_request_t object, which allows for +stages of tuning through through the **tune_request_t** object, which allows for more advanced tuning. In general, Using UHD's advanced tuning is highly recommended as it makes it easy to move the DC component out of your band-of-interest. This can be done by -passing your desired LO offset to the tune_request_t object, and letting UHD +passing your desired LO offset to the **tune_request_t** object, and letting UHD handle the rest. Tuning the receive chain: @@ -50,7 +50,7 @@ After tuning, the RF front-end will need time to settle into a usable state. Typically, this means that the local oscillators must be given time to lock before streaming begins. Lock time is not consistent; it varies depending upon the device and requested settings. After tuning and before streaming, the user -should wait for the "lo_locked" sensor to become true, or sleep for +should wait for the **lo_locked** sensor to become true or sleep for a conservative amount of time (perhaps a second). Pseudo-code for dealing with settling time after tuning on receive: @@ -69,7 +69,7 @@ Pseudo-code for dealing with settling time after tuning on receive: usrp->issue_stream_command(...); ------------------------------------------------------------------------ -Specifying the subdevice to use +Specifying the Subdevice to Use ------------------------------------------------------------------------ A subdevice specification string for USRP family devices is composed of: @@ -115,7 +115,7 @@ The frontend names are documented in the `Daughterboard Application Notes <./dboards.html>`_ ------------------------------------------------------------------------ -Overflow/Underflow notes +Overflow/Underflow Notes ------------------------------------------------------------------------ **Note:** The following overflow/underflow notes do not apply to USRP1, which does not support the advanced features available in newer products. @@ -132,26 +132,26 @@ and pushes an inline message packet into the receive stream. The host does not back-pressure the receive stream. When the kernel's socket buffer becomes full, it will drop subsequent packets. UHD detects the overflow as a discontinuity in the packet's sequence numbers, -and muxes an inline message packet into the receive stream. +and pushes an inline message packet into the receive stream. **Other devices**: The host back-pressures the receive stream. Therefore, overflows always occur in the device itself. -When the device's internal buffers become full, streaming is shutoff, +When the device's internal buffers become full, streaming is shut off, and an inline message packet is sent to the host. If the device was in continuous streaming mode, -the UHD will automatically restart streaming. +UHD will automatically restart streaming. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Underflow notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When transmitting, the device consumes samples at a constant rate. Underflow occurs when the host does not produce data fast enough. -When the UHD detects underflow, it prints an "U" to stdout, +When UHD detects underflow, it prints a "U" to stdout, and pushes a message packet into the async message stream. ------------------------------------------------------------------------ -Threading notes +Threading Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,7 +161,7 @@ For the most part, UHD is thread-safe. Please observe the following limitations: **Fast-path thread requirements:** -There are three fast-path methods for a device: send(), recv(), and recv_async_msg(). +There are three fast-path methods for a device: **send()**, **recv()**, and **recv_async_msg()**. All three methods are thread-safe and can be called from different thread contexts. For performance, the user should call each method from a separate thread context. These methods can also be used in a non-blocking fashion by using a timeout of zero. @@ -169,7 +169,7 @@ These methods can also be used in a non-blocking fashion by using a timeout of z **Slow-path thread requirements:** It is safe to change multiple settings simultaneously. However, this could leave the settings for a device in an uncertain state. -The is because changing one setting could have an impact on how a call affects other settings. +This is because changing one setting could have an impact on how a call affects other settings. Example: setting the channel mapping affects how the antennas are set. It is recommended to use at most one thread context for manipulating device settings. @@ -177,23 +177,23 @@ It is recommended to use at most one thread context for manipulating device sett Thread priority scheduling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When the UHD spawns a new thread it may try to boost the thread's scheduling priority. -When setting the priority fails, the UHD prints out an error. -This error is harmless, it simply means that the thread will have a normal scheduling priority. +When UHD spawns a new thread it may try to boost the thread's scheduling priority. +When setting the priority fails, UHD prints out an error. +This error is harmless; it simply means that the thread will have a normal scheduling priority. **Linux Notes:** Non-privileged users need special permission to change the scheduling priority. -Add the following line to */etc/security/limits.conf*: +Add the following line to **/etc/security/limits.conf**: :: @<my_group> - rtprio 99 -Replace <my_group> with a group to which your user belongs. -Settings will not take effect until the user has logged in and out. +Replace **<my_group>** with a group to which your user belongs. +Settings will not take effect until the user is in a different login session. ------------------------------------------------------------------------ -Misc notes +Miscellaneous Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -201,9 +201,9 @@ Support for dynamically loadable modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For a module to be loaded at runtime, it must be: -* found in the UHD_MODULE_PATH environment variable, -* installed into the <install-path>/share/uhd/modules directory, -* or installed into /usr/share/uhd/modules directory (unix only). +* found in the **UHD_MODULE_PATH** environment variable, +* installed into the **<install-path>/share/uhd/modules** directory, +* or installed into **/usr/share/uhd/modules** directory (UNIX only). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Disabling or redirecting prints to stdout @@ -211,7 +211,7 @@ Disabling or redirecting prints to stdout The user can disable the UHD library from printing directly to stdout by registering a custom message handler. The handler will intercept all messages, which can be dropped or redirected. Only one handler can be registered at a time. -Make "register_handler" your first call into UHD: +Make **register_handler** your first call into UHD: :: diff --git a/host/docs/gpsdo.rst b/host/docs/gpsdo.rst index f732aae63..1e9019a0f 100644 --- a/host/docs/gpsdo.rst +++ b/host/docs/gpsdo.rst @@ -11,11 +11,11 @@ to the Jackson Labs Firefly-1A device unless noted otherwise. ------------------------------------------------------------------------ Specifications ------------------------------------------------------------------------ -* Receiver type: 50 channel with WAAS, EGNOS, MSAS -* 10MHz ADEV: 1e-11 over >24h -* 1PPS RMS jitter: <50ns 1-sigma -* Holdover: <11us over 3h -* Phase noise: +* **Receiver type**: 50 channel with WAAS, EGNOS, MSAS +* **10MHz ADEV**: 1e-11 over >24h +* **1PPS RMS jitter**: <50ns 1-sigma +* **Holdover**: <11us over 3h +* **Phase noise**: * **1Hz:** -80dBc/Hz * **10Hz:** -110dBc/Hz @@ -25,25 +25,28 @@ Specifications **Antenna Types:** -The GPSDO is capable of supplying a 3V for active GPS antennas or supporting passive antennas +The GPSDO is capable of supplying a 3V for active GPS antennas or supporting passive antennas. ------------------------------------------------------------------------ -Installation instructions +Installation Instructions ------------------------------------------------------------------------ Installation instructions can be found here: -`www.ettus.com/downloads/gpsdo-kit.pdf <http://www.ettus.com/downloads/gpsdo-kit.pdf>`_ +`http://www.ettus.com/content/files/gpsdo-kit_2.pdf <http://www.ettus.com/content/files/gpsdo-kit_2.pdf>`_ ******************************************** -Post installation task (N-Series only) +Post-installation Task (N-Series only) ******************************************** -This is necessary if you require absolute GPS time in your application, + +**Note:** The following instructions are only necessary for UHD 3.4.* and below. + +This is necessary if you require absolute GPS time in your application or need to communicate with the GPSDO to obtain location, satellite info, etc. -If you only require 10MHz and PPS signals for reference or MIMO use, +If you only require 10MHz and PPS signals for reference or MIMO use (see the `Synchronization Application Notes <./sync.html>`_), it is not necessary to perform this step. To configure the USRP to communicate with the GPSDO, use the -usrp_burn_mb_eeprom utility: +**usrp_burn_mb_eeprom** utility: :: @@ -54,7 +57,7 @@ usrp_burn_mb_eeprom utility: ./usrp_burn_mb_eeprom --args=<optional device args> --key=gpsdo --val=none ------------------------------------------------------------------------ -Using the GPSDO in your application +Using the GPSDO in Your Application ------------------------------------------------------------------------ By default, if a GPSDO is detected at startup, the USRP will be configured to use it as a frequency and time reference. The internal VITA timestamp @@ -62,8 +65,8 @@ will be initialized to the GPS time, and the internal oscillator will be phase-locked to the 10MHz GPSDO reference. If the GPSDO is not locked to satellites, the VITA time will not be initialized. -GPS data is obtained through the mboard_sensors interface. To retrieve -the current GPS time, use the "gps_time" sensor: +GPS data is obtained through the **mboard_sensors** interface. To retrieve +the current GPS time, use the **gps_time** sensor: :: @@ -71,10 +74,10 @@ the current GPS time, use the "gps_time" sensor: The returned value will be the current epoch time, in seconds since January 1, 1970. This value is readily converted into human-readable -format using the time.h library in C, boost::posix_time in C++, etc. +format using the **time.h** library in C, **boost::posix_time** in C++, etc. Other information can be fetched as well. You can query the lock status -with the "gps_locked" sensor, as well as obtain raw NMEA sentences using -the "gps_gprmc", and "gps_gpgga" sensors. Location -information can be parsed out of the "gps_gpgga" sensor by using gpsd or +with the **gps_locked** sensor, as well as obtain raw NMEA sentences using +the **gps_gprmc**, and **gps_gpgga** sensors. Location +information can be parsed out of the **gps_gpgga** sensor by using **gpsd** or another NMEA parser. diff --git a/host/docs/identification.rst b/host/docs/identification.rst index deda61531..a5e60e7f9 100644 --- a/host/docs/identification.rst +++ b/host/docs/identification.rst @@ -9,9 +9,9 @@ Identifying USRPs ------------------------------------------------------------------------ Devices are addressed through key/value string pairs. These string pairs can be used to narrow down the search for a specific device or group of devices. -Most UHD utility applications and examples have a --args parameter that takes a device address; -where the device address is expressed as a delimited string. -See the documentation in types/device_addr.hpp for reference. +Most UHD utility applications and examples have an **--args** parameter that takes a device address, which is expressed as a delimited string. + +See the documentation in **types/device_addr.hpp** for reference. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Common device identifiers @@ -33,8 +33,8 @@ Every device has several ways of identifying it on the host system: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device discovery via command line ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Devices attached to your system can be discovered using the "uhd_find_devices" program. -The find devices program scans your system for supported devices and prints +Devices attached to your system can be discovered using the **uhd_find_devices** program. +This program scans your system for supported devices and prints out an enumerated list of discovered devices and their addresses. The list of discovered devices can be narrowed down by specifying device address args. @@ -55,14 +55,14 @@ Device address arguments can be supplied to narrow the scope of the search. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device discovery through the API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The device::find() API call searches for devices and returns a list of discovered devices. +The **device::find()** API call searches for devices and returns a list of discovered devices. :: uhd::device_addr_t hint; //an empty hint discovers all devices uhd::device_addrs_t dev_addrs = uhd::device::find(hint); -The hint argument can be populated to narrow the scope of the search. +The **hint** argument can be populated to narrow the scope of the search. :: @@ -79,9 +79,9 @@ The hint argument can be populated to narrow the scope of the search. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device properties ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Properties of devices attached to your system can be probed with the "uhd_usrp_probe" program. -The usrp probe program constructs an instance of the device and prints out its properties; -properties such as detected daughter-boards, frequency range, gain ranges, etc... +Properties of devices attached to your system can be probed with the **uhd_usrp_probe** program. +This program constructs an instance of the device and prints out its properties, +such as detected daughterboards, frequency range, gain ranges, etc... **Usage:** :: @@ -97,7 +97,7 @@ The USRP can then be identified via name, rather than a difficult to remember se A name has the following properties: * is composed of ASCII characters -* is between 0 and 20 characters +* is 0-20 characters * is not required to be unique ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ Run the following commands: Discovery via name ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The keyword "name" can be used to narrow the scope of the search. +The keyword **name** can be used to narrow the scope of the search. Example with the find devices utility: :: diff --git a/host/docs/images.rst b/host/docs/images.rst index a25268990..eaddfdf1d 100644 --- a/host/docs/images.rst +++ b/host/docs/images.rst @@ -8,7 +8,7 @@ UHD - Firmware and FPGA Image Application Notes Images Overview ------------------------------------------------------------------------ Every USRP device must be loaded with special firmware and FPGA images. -The methods of loading images into the device varies among devices: +The methods of loading images into the device vary among devices: * **USRP1:** The host code will automatically load the firmware and FPGA at runtime. * **USRP2:** The user must manually write the images onto the USRP2 SD card. @@ -17,10 +17,15 @@ The methods of loading images into the device varies among devices: * **USRP-B Series:** The host code will automatically load the FPGA at runtime. ------------------------------------------------------------------------ -Pre-built images +Pre-built Images ------------------------------------------------------------------------ Pre-built images are available for download. + +* `Master Branch images <http://files.ettus.com/binaries/master_images/>`_ +* `Maint Branch images <http://files.ettus.com/binaries/maint_images/>`_ +* `Next Branch images <http://files.ettus.com/binaries/next_images/>`_ + See the UHD wiki for the download link. The pre-built images come in two forms: @@ -31,8 +36,9 @@ The pre-built images come in two forms: ^^^^^^^^^^^^^^^^^^^^^^ Platform installers ^^^^^^^^^^^^^^^^^^^^^^ -The UNIX-based installers will install the images into /usr/share/uhd/images. -On windows, the images will be installed to <install-path>/share/uhd/images. +The UNIX-based installers will install the images into **/usr/share/uhd/images**. + +The Windows installers will install the images into **C:/Program Files/UHD/share/uhd/images**. ^^^^^^^^^^^^^^^^^^^^^^ Archive install @@ -42,52 +48,53 @@ When installing images from an archive, there are two options: **Option 1:** Unpack the archive into the UHD installation prefix. -The UHD will always search <install-path>/share/uhd/images for image files. -Where <install-path> was set by the CMAKE_INSTALL_PREFIX at configure-time. +UHD will always search **<install-path>/share/uhd/images** for image files. +Where **<install-path>** was set by the **CMAKE_INSTALL_PREFIX** at configure-time. **Option 2:** -Unpack the archive anywhere and set the UHD_IMAGE_PATH environment variable. -The UHD_IMAGE_PATH may contain a list of directories to search for image files. +Unpack the archive anywhere and set the **UHD_IMAGES_PATH** environment variable. +**UHD_IMAGES_PATH** may contain a list of directories to search for image files. ------------------------------------------------------------------------ -Building images +Building Images ------------------------------------------------------------------------ The UHD source repository comes with the source code necessary to build both firmware and FPGA images for all supported devices. -The build commands for a particular image can be found in <uhd-repo-path>/images/Makefile. + +The build commands for a particular image can be found in **<uhd-repo-path>/images/Makefile**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Xilinx FPGA builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Xilinx ISE 12.x and up is required to build the Xilinx FPGA images. -The build requires that you have a unix-like environment with make. -Make sure that xtclsh from the Xilinx ISE bin directory is in your $PATH. +The build requires that you have a UNIX-like environment with **Make**. +Make sure that **xtclsh** from the Xilinx ISE bin directory is in your **$PATH**. -See <uhd-repo-path>/fpga/usrp2/top/* +See **<uhd-repo-path>/fpga/usrp2/top/**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ZPU firmware builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ZPU GCC compiler is required to build the ZPU firmware images. -The build requires that you have a unix-like environment with cmake and make. -Make sure that zpu-elf-gcc is in your $PATH. +The build requires that you have a UNIX-like environment with **CMake** and **Make**. +Make sure that **zpu-elf-gcc** is in your **$PATH**. -See <uhd-repo-path>/firmware/zpu +See **<uhd-repo-path>/firmware/zpu**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Altera FPGA builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Quartus is required to build the Altera FPGA images. -Pre-built images can also be found in <uhd-repo-path>/fpga/usrp1/rbf +Pre-built images can also be found in **<uhd-repo-path>/fpga/usrp1/rbf**. -See <uhd-repo-path>/fpga/usrp1/toplevel/* +See **<uhd-repo-path>/fpga/usrp1/toplevel/***. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FX2 firmware builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The sdcc compiler is required to build the FX2 firmware images. -The build requires that you have a unix-like environment with cmake and make. +The SDCC compiler is required to build the FX2 firmware images. +The build requires that you have a UNIX-like environment with **CMake** and **Make**. -See <uhd-repo-path>/firmware/fx2 +See **<uhd-repo-path>/firmware/fx2**. diff --git a/host/docs/index.rst b/host/docs/index.rst index f881e8585..00b1c9618 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -2,18 +2,20 @@ UHD - USRP Hardware Driver ======================================================================== -The UHD is the "Universal Software Radio Peripheral" hardware driver. -The goal of the UHD is to provide a host driver and API for current and future Ettus Research products. -Users will be able to use the UHD driver standalone or with 3rd party applications. +UHD is the "Universal Software Radio Peripheral" hardware driver. +The goal of UHD is to provide a host driver and API for current and future Ettus Research products. +Users will be able to use the UHD driver standalone or with third-party applications. ------------------------------------------------------------------------ Contents ------------------------------------------------------------------------ -^^^^^^^^^^^^^^^^^^^^^ -Building the UHD -^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Building and Installing UHD +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * `Build Guide <./build.html>`_ +* `Installation Guide (Linux) <http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_Linux>`_ +* `Installation Guide (Windows) <http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_Windows>`_ ^^^^^^^^^^^^^^^^^^^^^ Application Notes @@ -23,9 +25,9 @@ Application Notes * `Firmware and FPGA Image Notes <./images.html>`_ * `USRP1 Application Notes <./usrp1.html>`_ * `USRP2 Application Notes <./usrp2.html>`_ -* `USRP-N2XX Series Application Notes <./usrp2.html>`_ -* `USRP-B1XX Series Application Notes <./usrp_b1xx.html>`_ -* `USRP-E1XX Series Application Notes <./usrp_e1xx.html>`_ +* `USRP-N2X0 Series Application Notes <./usrp2.html>`_ +* `USRP-B100 Series Application Notes <./usrp_b100.html>`_ +* `USRP-E1X0 Series Application Notes <./usrp_e1x0.html>`_ * `Daughterboard Application Notes <./dboards.html>`_ * `Transport Application Notes <./transport.html>`_ * `Synchronization Application Notes <./sync.html>`_ @@ -37,5 +39,4 @@ API Documentation ^^^^^^^^^^^^^^^^^^^^^ * `Doxygen <./../../doxygen/html/index.html>`_ * `Using the API <./coding.html>`_ -* `Device streaming <./stream.html>`_ - +* `Device Streaming <./stream.html>`_ diff --git a/host/docs/stream.rst b/host/docs/stream.rst index 9ffec22e5..a00ef88ee 100644 --- a/host/docs/stream.rst +++ b/host/docs/stream.rst @@ -5,15 +5,15 @@ UHD - Device streaming .. contents:: Table of Contents ------------------------------------------------------------------------ -Introduction to streaming +Introduction to Streaming ------------------------------------------------------------------------ The concept of streaming refers to the transportation of samples between host and device. A stream is an object that facilitates streaming between host application and device. -A RX stream allows the user to receive samples from the device. +An RX stream allows the user to receive samples from the device. A TX stream allows the user to transmit samples to the device. ------------------------------------------------------------------------ -Link layer encapsulation +Link Layer Encapsulation ------------------------------------------------------------------------ The VITA49 standard provides encapsulation for sample data across a link layer. On all generation2 hardware, samples are encapsulated into VRT IF data packets. @@ -22,12 +22,12 @@ Sample decoration is exposed to the user in the form of RX and TX metadata struc The length of an IF data packet can be limited by several factors: -* MTU of the link layer - network card, network switch -* Buffering on the host - frame size in a ring buffer -* Buffering on the device - size of BRAM FIFOs +* **MTU of the link layer:** network card, network switch +* **Buffering on the host:** frame size in a ring buffer +* **Buffering on the device:** size of BRAM FIFOs ------------------------------------------------------------------------ -Data types +Data Types ------------------------------------------------------------------------ There are two important data types to consider when streaming: @@ -38,15 +38,15 @@ There are two important data types to consider when streaming: The host/CPU data type ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The host data type refers to the format of samples used in the host for baseband processing. -Typically, the data type is complex baseband such as normalized complex-float32 or complex-int16. +Typically, the data type is complex baseband such as normalized **complex-float32** or **complex-int16**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The link-layer data type ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The link-layer or "over-the-wire" data type refers to the format of the samples sent through the link. -Typically, this data type is complex-int16. -However, To increase throughput over the link-layer, -at the expense of precision, complex-int8 may be used. +Typically, this data type is **complex-int16**. +However, to increase throughput over the link-layer, +at the expense of precision, **complex-int8** may be used. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Conversion @@ -54,6 +54,6 @@ Conversion The user may request arbitrary combinations of host and link data types; however, not all combinations are supported. The user may register custom data type formats and conversion routines. -See uhd/convert.hpp for futher documentation. +See **uhd/convert.hpp** for futher documentation. -TODO provide example of convert API +TODO: provide example of convert API diff --git a/host/docs/sync.rst b/host/docs/sync.rst index 55c9f81f0..5abd39f86 100644 --- a/host/docs/sync.rst +++ b/host/docs/sync.rst @@ -12,13 +12,13 @@ or other applications requiring multiple USRPs operating synchronously. which does not support the advanced features available in newer products. ------------------------------------------------------------------------ -Common reference signals +Common Reference Signals ------------------------------------------------------------------------ USRPs take two reference signals in order to synchronize clocks and time: * A 10MHz reference to provide a single frequency reference for both devices. -* A pulse-per-second (1PPS) to synchronize the sample time across devices. -* Or, the MIMO cable transmits an encoded time message from one device to another. +* A pulse-per-second (PPS) to synchronize the sample time across devices. +* A MIMO cable transmits an encoded time message from one device to another. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PPS and 10 MHz reference signals @@ -36,7 +36,7 @@ However, some USRP models can provide these signals from an optional internal GP Sometimes the delay on the PPS signal will cause it to arrive inside the timing margin the FPGA sampling clock, causing PPS edges to be separated by less or more than 100 million cycles of the FPGA clock. If this is the case, -you can change the edge reference of the PPS signal with this special parameter: +you can change the edge reference of the PPS signal with this parameter: :: @@ -44,7 +44,7 @@ you can change the edge reference of the PPS signal with this special parameter: **Note2:** For users generating their own signals for the external SMA connectors, -the pulse-per-second should be clocked from the 10MHz reference. +the PPS should be clocked from the 10MHz reference. See the application notes for your device for specific signal requirements. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ MIMO cable reference signals ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use the MIMO expansion cable to share reference sources (USRP2 and N-Series). The MIMO cable can be used synchronize one device to another device. -Users of the MIMO cable may use method 1 to synchronize multiple pairs of devices. +Users of the MIMO cable may use Method 1 (explained below) to synchronize multiple pairs of devices. :: @@ -60,13 +60,13 @@ Users of the MIMO cable may use method 1 to synchronize multiple pairs of device usrp->set_time_source("mimo"); ------------------------------------------------------------------------ -Synchronizing the device time +Synchronizing the Device Time ------------------------------------------------------------------------ The purpose of the PPS signal is to synchronously latch a time into the device. -You can use the set_time_next_pps(...) function to either initialize the sample time to 0, -or to an absolute time such as GPS time or UTC time. +You can use the **set_time_next_pps(...)** function to either initialize the sample time to 0 +or an absolute time, such as GPS time or UTC time. For the purposes of synchronizing devices, -it doesn't matter what time you initialize to when using set_time_next_pps(...). +it doesn't matter what time you initialize to when using **set_time_next_pps(...)**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method 1 - poll the USRP time registers @@ -85,7 +85,7 @@ When the last PPS time increments, the user can determine that a PPS has occurre ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method 2 - query the GPSDO for seconds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Most GPSDO can be configured to output a NMEA string over the serial port once every PPS. +Most GPSDOs can be configured to output a NMEA string over the serial port once every PPS. The user can wait for this string to determine the PPS edge, and the user can also parse this string to determine GPS time: @@ -117,7 +117,7 @@ The slave device will automatically synchronize to the time on the master device See the `MIMO Cable Application Notes <./usrp2.html#using-the-mimo-cable>`_ for more detail. ------------------------------------------------------------------------ -Synchronizing channel phase +Synchronizing Channel Phase ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,8 +152,41 @@ For transmit, a burst is started when the user calls send(). The metadata should //send a single packet size_t num_tx_samps = tx_streamer->send(buffs, samps_to_send, md); +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Align LOs in the front-end (SBX/WBX + N-Series) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Using timed commands, multiple frontends can be tuned at a specific time. +This timed-tuning ensures that the phase offsets between VCO/PLL chains +will remain constant after each re-tune. See notes below: + +* There is a random phase offset between any two frontends +* This phase offset is different for different LO frequencies +* This phase offset remains constant after retuning + + * Due to divider, WBX phase offset will be randomly +/- 180 deg after re-tune + +* This phase offset will drift over time due to thermal and other characteristics +* Periodic calibration will be necessary for phase-coherent applications + +Code snippet example, tuning with timed commands: +:: + + //we will tune the frontends in 100ms from now + uhd::time_spec_t cmd_time = usrp->get_time_now() + uhd::time_spec_t(0.1); + + //sets command time on all devices + //the next commands are all timed + usrp->set_command_time(cmd_time); + + //tune channel 0 and channel 1 + usrp->set_rx_freq(1.03e9, 0/*ch0*/); + usrp->set_rx_freq(1.03e9, 1/*ch1*/); + + //end timed commands + usrp->set_clear_time(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Align LOs in the front-end +Align LOs in the front-end (others) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ After tuning the RF front-ends, each local oscillator may have a random phase offset due to the dividers diff --git a/host/docs/transport.rst b/host/docs/transport.rst index 7dc465b4c..2e39e75d1 100644 --- a/host/docs/transport.rst +++ b/host/docs/transport.rst @@ -17,7 +17,7 @@ that are known to perform well on a variety of systems. The transport parameters are defined below for the various transports in the UHD: ------------------------------------------------------------------------ -UDP transport (sockets) +UDP Transport (Sockets) ------------------------------------------------------------------------ The UDP transport is implemented with user-space sockets. This means standard Berkeley sockets API using send()/recv(). @@ -33,15 +33,15 @@ The following parameters can be used to alter the transport's default behavior: * **num_send_frames:** The number of send buffers to allocate **Note1:** -num_recv_frames does not affect performance. +**num_recv_frames** does not affect performance. **Note2:** -num_send_frames does not affect performance. +**num_send_frames** does not affect performance. **Note3:** -recv_frame_size and send_frame_size can be used to +**recv_frame_size** and **send_frame_size** can be used to increase or decrease the maximum number of samples per packet. -The frame sizes default to an MTU of 1472 bytes per IP/UDP packet, +The frame sizes default to an MTU of 1472 bytes per IP/UDP packet and may be increased if permitted by your network hardware. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,8 +58,8 @@ The following mechanisms affect the transmission of periodic update packets: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Resize socket buffers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -It may be useful increase the size of the socket buffers to -move the burden of buffering samples into the kernel, or to +It may be useful to increase the size of the socket buffers to +move the burden of buffering samples into the kernel or to buffer incoming samples faster than they can be processed. However, if your application cannot process samples fast enough, no amount of buffering can save you. @@ -74,7 +74,7 @@ The following parameters can be used to alter socket's buffer sizes: Latency Optimization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Latency is a measurement of the time it takes a sample to travel between the host and device. -Most computer hardware and software is bandwidth optimized which may negatively affect latency. +Most computer hardware and software is bandwidth optimized, which may negatively affect latency. If your application has strict latency requirements, please consider the following notes: **Note1:** @@ -92,7 +92,7 @@ Also, consult: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Linux specific notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On linux, the maximum buffer sizes are capped by the sysctl values +On Linux, the maximum buffer sizes are capped by the sysctl values **net.core.rmem_max** and **net.core.wmem_max**. To change the maximum values, run the following commands: :: @@ -100,22 +100,33 @@ To change the maximum values, run the following commands: sudo sysctl -w net.core.rmem_max=<new value> sudo sysctl -w net.core.wmem_max=<new value> -Set the values permanently by editing */etc/sysctl.conf* +Set the values permanently by editing **/etc/sysctl.conf**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Windows specific notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Windows, it is important to change the default UDP behavior such that +**UDP send fast-path:** +It is important to change the default UDP behavior such that 1500 byte packets still travel through the fast path of the sockets stack. -FastSendDatagramThreshold registry key to change documented here: +This can be adjusted with the FastSendDatagramThreshold registry key: -* http://www.microsoft.com/windows/windowsmedia/howto/articles/optimize_web.aspx#appendix_e +* FastSendDatagramThreshold registry key documented here: + + * http://www.microsoft.com/windows/windowsmedia/howto/articles/optimize_web.aspx#appendix_e + +* Double click and run <install-path>/share/uhd/FastSendDatagramThreshold.reg +* A system reboot is recommended after the registry key change. + +**Power profile:** +The Windows power profile can seriously impact instantaneous bandwidth. +Application can take time to ramp-up to full performance capability. +It is recommended that users set the power profile to "high performance". ------------------------------------------------------------------------ -USB transport (libusb) +USB Transport (LibUSB) ------------------------------------------------------------------------ -The USB transport is implemented with libusb. -Libusb provides an asynchronous API for USB bulk transfers. +The USB transport is implemented with LibUSB. +LibUSB provides an asynchronous API for USB bulk transfers. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Transport parameters @@ -145,9 +156,9 @@ Install USB driver (Windows) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A driver package must be installed to use a USB-based product with UHD: -* Download the driver from the UHD wiki page. -* Unzip the file into a known location. We will refer to this as the <directory>. -* Open the device manager and plug-in the USRP. You will see an unrecognized USB device in the device manager. +* Download the driver from the UHD wiki page `here <http://files.ettus.com/binaries/misc/erllc_uhd_winusb_driver.zip>`_. +* Unzip the file into a known location. We will refer to this as the **<directory>**. +* Open the device manager and plug in the USRP. You will see an unrecognized USB device in the device manager. * Right click on the unrecognized USB device and select update/install driver software (may vary for your OS). -* In the driver installation wizard, select "browse for driver", browse to the <directory>, and select the .inf file. +* In the driver installation wizard, select "browse for driver", browse to the **<directory>**, and select the **.inf** file. * Continue through the installation wizard until the driver is installed. diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst index 597b5b17f..c1fdec146 100644 --- a/host/docs/usrp1.rst +++ b/host/docs/usrp1.rst @@ -5,7 +5,18 @@ UHD - USRP1 Application Notes .. contents:: Table of Contents ------------------------------------------------------------------------ -Specify a non-standard image +Comparative features list +------------------------------------------------------------------------ + +* 2 transceiver card slots +* 2 RX DDC chains in FPGA +* 2 TX DUC chains in FPGA (no TX CORDIC -> uses DAC) +* 64 MHz fixed clock rate +* sc16 sample modes +* sc8 sample mode - RX only + +------------------------------------------------------------------------ +Specify a Non-standard Image ------------------------------------------------------------------------ The standard USRP1 images installer comes with two FPGA images: * **usrp1_fpga.rbf:** 2 DDCs + 2 DUCs @@ -31,7 +42,7 @@ Example device address string representations to specify non-standard firmware a fpga=usrp1_fpga_4rx.rbf, fw=usrp1_fw_custom.ihx ------------------------------------------------------------------------ -Missing and emulated features +Missing and Emulated Features ------------------------------------------------------------------------ The USRP1 FPGA does not have the necessary space to support the advanced streaming capabilities that are possible with the newer USRP devices. @@ -53,7 +64,7 @@ List of emulated features * Notification on broken chain error **Note:** -These emulated features rely on the host system's clock for timed operations, +These emulated features rely on the host system's clock for timed operations and therefore may not have sufficient precision for the application. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,17 +73,17 @@ List of missing features * Start of burst flags for transmit/receive ------------------------------------------------------------------------ -Hardware setup notes +Hardware Setup Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ External clock modification ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The USRP can be modified to accept an external clock reference instead of the 64MHz onboard reference. - * Solder SMA (LTI-SASF54GT) connector to J2001 - * Move 0 ohm 0603 resistor R2029 to R2030 - * Move 0.01uF 0603 capacitor C925 to C926 - * Remove 0.01uF 0603 capacitor C924 + * Solder SMA (**LTI-SASF54GT**) connector to **J2001**. + * Move 0 ohm 0603 resistor **R2029** to **R2030**. + * Move 0.01uF 0603 capacitor **C925** to **C926**. + * Remove 0.01uF 0603 capacitor **C924**. The new external clock needs to be a square wave between +7dBm and +15dBm diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index d81440b07..8e9aa6d50 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -1,15 +1,31 @@ ======================================================================== -UHD - USRP2 and N Series Application Notes +UHD - USRP2 and N2X0 Series Application Notes ======================================================================== .. contents:: Table of Contents ------------------------------------------------------------------------ -Load the images onto the SD card (USRP2 only) +Comparative features list +------------------------------------------------------------------------ + +* 1 transceiver card slot +* 2 RX DDC chains in FPGA +* 1 TX DUC chain in FPGA +* Timed commands in FPGA (N2x0 only) +* Timed sampling in FPGA +* External PPS reference +* External 10MHz reference +* MIMO cable shared reference +* Fixed 100 MHz clock rate +* Internal GPSDO option (N2x0 only) +* sc8 and sc16 sample modes + +------------------------------------------------------------------------ +Load the Images onto the SD card (USRP2 only) ------------------------------------------------------------------------ **Warning!** -Use the usrp2_card_burner.py with caution. If you specify the wrong device node, -you could overwrite your hard drive. Make sure that --dev= specifies the SD card. +Use **usrp2_card_burner.py** with caution. If you specify the wrong device node, +you could overwrite your hard drive. Make sure that **--dev=** specifies the SD card. **Warning!** It is possible to use 3rd party SD cards with the USRP2. @@ -21,7 +37,7 @@ However, certain types of SD cards will not interface with the CPLD: For these reasons, we recommend that you use the SD card that was supplied with the USRP2. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the card burner tool (unix) +Use the card burner tool (UNIX) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -33,19 +49,19 @@ Use the card burner tool (unix) sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fpga=<path_to_fpga_image> sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fw=<path_to_firmware_image> -Use the *--list* option to get a list of possible raw devices. +Use the **--list** option to get a list of possible raw devices. The list result will filter out disk partitions and devices too large to be the sd card. The list option has been implemented on Linux, Mac OS X, and Windows. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the card burner tool (windows) +Use the card burner tool (Windows) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: <path_to_python.exe> <install-path>/share/uhd/utils/usrp2_card_burner_gui.py ------------------------------------------------------------------------ -Load the images onto the on-board flash (USRP-N Series only) +Load the Images onto the On-board Flash (USRP-N Series only) ------------------------------------------------------------------------ The USRP-N Series can be reprogrammed over the network to update or change the firmware and FPGA images. @@ -58,7 +74,7 @@ Determine the revision number from the sticker on the rear of the chassis. Use this number to select the correct FPGA image for your device. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the net burner tool (unix) +Use the net burner tool (UNIX) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -88,22 +104,22 @@ The safe-mode button is a pushbutton switch (S2) located inside the enclosure. To boot into the safe image, hold-down the safe-mode button while power-cycling the device. Continue to hold-down the button until the front-panel LEDs blink and remain solid. -When in safe-mode, the USRP-N device will always have the IP address 192.168.10.2 +When in safe-mode, the USRP-N device will always have the IP address **192.168.10.2**. ------------------------------------------------------------------------ -Setup networking +Setup Networking ------------------------------------------------------------------------ -The USRP2 only supports gigabit ethernet, +The USRP2 only supports Gigabit Ethernet and will not work with a 10/100 Mbps interface. However, a 10/100 Mbps interface can be connected indirectly -to a USRP2 through a gigabit ethernet switch. +to a USRP2 through a Gigabit Ethernet switch. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the host interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The USRP2 communicates at the IP/UDP layer over the gigabit ethernet. -The default IP address of the USRP2 is **192.168.10.2** -You will need to configure the host's ethernet interface with a static IP +The default IP address of the USRP2 is **192.168.10.2**. +You will need to configure the host's Ethernet interface with a static IP address to enable communication. An address of **192.168.10.1** and a subnet mask of **255.255.255.0** is recommended. @@ -113,15 +129,15 @@ On a Linux system, you can set a static IP address very easily by using the sudo ifconfig <interface> 192.168.10.1 -Note that <interface> is usually something like 'eth0'. You can discover the -names of the network interfaces in your computer by running 'ifconfig' without +Note that **<interface>** is usually something like **eth0**. You can discover the +names of the network interfaces in your computer by running **ifconfig** without any parameters: :: ifconfig -a **Note:** -When using the UHD, if an IP address for the USRP2 is not specified, +When using UHD, if an IP address for the USRP2 is not specified, the software will use UDP broadcast packets to locate the USRP2. On some systems, the firewall will block UDP broadcast packets. It is recommended that you change or disable your firewall settings. @@ -129,23 +145,23 @@ It is recommended that you change or disable your firewall settings. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Multiple devices per host ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For maximum throughput, one ethernet interface per USRP2 is recommended, -although multiple devices may be connected via a gigabit ethernet switch. -In any case, each ethernet interface should have its own subnet, +For maximum throughput, one Ethernet interface per USRP2 is recommended, +although multiple devices may be connected via a Gigabit Ethernet switch. +In any case, each Ethernet interface should have its own subnet, and the corresponding USRP2 device should be assigned an address in that subnet. Example: **Configuration for USRP2 device 0:** -* Ethernet interface IPv4 address: 192.168.10.1 -* Ethernet interface subnet mask: 255.255.255.0 -* USRP2 device IPv4 address: 192.168.10.2 +* Ethernet interface IPv4 address: **192.168.10.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP2 device IPv4 address: **192.168.10.2** **Configuration for USRP2 device 1:** -* Ethernet interface IPv4 address: 192.168.20.1 -* Ethernet interface subnet mask: 255.255.255.0 -* USRP2 device IPv4 address: 192.168.20.2 +* Ethernet interface IPv4 address: **192.168.20.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP2 device IPv4 address: **192.168.20.2** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Change the USRP2's IP address @@ -157,7 +173,7 @@ You may need to change the USRP2's IP address for several reasons: * to set a known IP address into USRP2 (in case you forgot) **Method 1:** -To change the USRP2's IP address +To change the USRP2's IP address, you must know the current address of the USRP2, and the network must be setup properly as described above. Run the following commands: @@ -168,7 +184,7 @@ Run the following commands: **Method 2 (Linux Only):** This method assumes that you do not know the IP address of your USRP2. -It uses raw ethernet packets to bypass the IP/UDP layer to communicate with the USRP2. +It uses raw Ethernet packets to bypass the IP/UDP layer to communicate with the USRP2. Run the following commands: :: @@ -176,23 +192,23 @@ Run the following commands: sudo ./usrp2_recovery.py --ifc=eth0 --new-ip=192.168.10.3 ------------------------------------------------------------------------ -Communication problems +Communication Problems ------------------------------------------------------------------------ When setting up a development machine for the first time, you may have various difficulties communicating with the USRP device. -The following tips are designed to help narrow-down and diagnose the problem. +The following tips are designed to help narrow down and diagnose the problem. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: no control response ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is a common error that occurs when you have set the subnet of your network interface to a different subnet than the network interface of the USRP. For -example, if your network interface is set to 192.168.20.1, and the USRP is -192.168.10.2 (note the difference in the third numbers of the IP addresses), you +example, if your network interface is set to **192.168.20.1**, and the USRP is +**192.168.10.2** (note the difference in the third numbers of the IP addresses), you will likely see a 'no control response' error message. Fixing this is simple - just set the your host PC's IP address to the same -subnet as your USRP. Instructions for setting your IP address are in the +subnet as that of your USRP. Instructions for setting your IP address are in the previous section of this documentation. @@ -200,19 +216,19 @@ previous section of this documentation. Firewall issues ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When the IP address is not specified, -the device discovery sends broadcast UDP packets from each ethernet interface. +the device discovery broadcasts UDP packets from each ethernet interface. Many firewalls will block the replies to these broadcast packets. -If disabling your system's firewall, -or specifying the IP address yeilds a discovered device, +If disabling your system's firewall +or specifying the IP address yields a discovered device, then your firewall may be blocking replies to UDP broadcast packets. -If this is the case, we recommend that you disable the firewall, -or create a rule to allow all incoming packets with UDP source port 49152. +If this is the case, we recommend that you disable the firewall +or create a rule to allow all incoming packets with UDP source port **49152**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ping the device ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The USRP will reply to icmp echo requests. -A successful ping response means that the device has booted properly, +The USRP will reply to ICMP echo requests. +A successful ping response means that the device has booted properly and that it is using the expected IP address. :: @@ -222,12 +238,12 @@ and that it is using the expected IP address. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Monitor the serial output ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Read the serial port to get debug verbose from the embedded microcontroller. +Read the serial port to get debug verbose output from the embedded microcontroller. The microcontroller prints useful information about IP addresses, MAC addresses, control packets, fast-path settings, and bootloading. Use a standard USB to 3.3v-level serial converter at 230400 baud. -Connect GND to the converter ground, and connect TXD to the converter receive. -The RXD pin can be left unconnected as this is only a one-way communication. +Connect **GND** to the converter ground, and connect **TXD** to the converter receive. +The **RXD** pin can be left unconnected as this is only a one-way communication. * **USRP2:** Serial port located on the rear edge * **N210:** Serial port located on the left side @@ -235,10 +251,10 @@ The RXD pin can be left unconnected as this is only a one-way communication. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Monitor the host network traffic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use wireshark to monitor packets sent to and received from the device. +Use Wireshark to monitor packets sent to and received from the device. ------------------------------------------------------------------------ -Addressing the device +Addressing the Device ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -248,9 +264,9 @@ In a single-device configuration, the USRP device must have a unique IPv4 address on the host computer. The USRP can be identified through its IPv4 address, resolvable hostname, or by other means. See the application notes on `device identification <./identification.html>`_. -Use this addressing scheme with the *single_usrp* interface. +Use this addressing scheme with the **single_usrp** interface. -Example device address string representation for a USRP2 with IPv4 address 192.168.10.2 +Example device address string representation for a USRP2 with IPv4 address **192.168.10.2**: :: @@ -263,12 +279,12 @@ In a multi-device configuration, each USRP device must have a unique IPv4 address on the host computer. The device address parameter keys must be suffixed with the device index. Each parameter key should be of the format <key><index>. -Use this addressing scheme with the *multi_usrp* interface. +Use this addressing scheme with the **multi_usrp** interface. * The order in which devices are indexed corresponds to the indexing of the transmit and receive channels. * The key indexing provides the same granularity of device identification as in the single device case. -Example device address string representation for 2 USRP2s with IPv4 addresses 192.168.10.2 and 192.168.20.2 +Example device address string representation for 2 USRP2s with IPv4 addresses **192.168.10.2** and **192.168.20.2**: :: addr0=192.168.10.2, addr1=192.168.20.2 @@ -277,31 +293,31 @@ Example device address string representation for 2 USRP2s with IPv4 addresses 19 Using the MIMO Cable ------------------------------------------------------------------------ The MIMO cable allows two USRP devices to share reference clocks, -time synchronization, and the ethernet interface. -One of the devices will sink its clock and time references to the MIMO cable. +time synchronization, and the Ethernet interface. +One of the devices will sync its clock and time references to the MIMO cable. This device will be referred to as the slave, and the other device, the master. * The slave device acquires the clock and time references from the master device. * The master and slave may be used individually or in a multi-device configuration. -* External clocking is optional, and should only be supplied to the master device. +* External clocking is optional and should only be supplied to the master device. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Shared ethernet mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In shared ethernet mode, -only one device in the configuration can be attached to the ethernet. +In shared Ethernet mode, +only one device in the configuration can be attached to the Tthernet. * Clock reference, time reference, and data are communicated over the MIMO cable. -* Both master and slave must have different IPv4 addresses in the same subnet. +* Master and slave must have different IPv4 addresses in the same subnet. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Dual ethernet mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In dual ethernet mode, -both devices in the configuration must be attached to the ethernet. +In dual Ethernet mode, +both devices in the configuration must be attached to the Ethernet. * Only clock reference and time reference are communicated over the MIMO cable. -* Both master and slave must have different IPv4 addresses in different subnets. +* The master and slave must have different IPv4 addresses in different subnets. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Configuring the slave @@ -315,8 +331,57 @@ the following clock configuration must be set on the slave device: clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; usrp->set_clock_config(clock_config, slave_index); + +------------------------------------------------------------------------ +Alternative stream destination +------------------------------------------------------------------------ +It is possible to program the USRP to send RX packets to an alternative IP/UDP destination. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the subnet and gateway +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use an alternative streaming destination, +the device needs to be able to determine if the destination address +is within its subnet, and ARP appropriately. +Therefore, the user should ensure that subnet and gateway addresses +have been programmed into the device's EEPROM. + +Run the following commands: +:: + + cd <install-path>/share/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --key=subnet --val=255.255.255.0 + ./usrp_burn_mb_eeprom --args=<optional device args> --key=gateway --val=192.168.10.1 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Create a receive streamer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the stream args "addr" and "port" values to the alternative destination. +Packets will be sent to this destination when the user issues a stream command. + +:: + + //create a receive streamer, host type does not matter + uhd::stream_args_t stream_args("fc32"); + + //resolvable address and port for a remote udp socket + stream_args.args["addr"] = "192.168.10.42"; + stream_args.args["port"] = "12345"; + + //create the streamer + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + //issue stream command + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = true; + usrp->issue_stream_cmd(stream_cmd); + +**Note:** +Calling recv() on this streamer object should yield a timeout. + ------------------------------------------------------------------------ -Hardware setup notes +Hardware Setup Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -336,8 +401,8 @@ The LEDs reveal the following about the state of the device: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ref Clock - 10MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Using an external 10MHz reference clock, square wave will offer the best phase -noise performance, but sinusoid is acceptable. The reference clock requires the following power level: +Using an external 10MHz reference clock, a square wave will offer the best phase +noise performance, but a sinusoid is acceptable. The reference clock requires the following power level: * **USRP2** 5 to 15dBm * **N2XX** 0 to 15dBm @@ -353,7 +418,7 @@ Using a PPS signal for timestamp synchronization requires a square wave signal w Test the PPS input with the following app: -* <args> are device address arguments (optional if only one USRP is on your machine) +* **<args>** are device address arguments (optional if only one USRP is on your machine) :: @@ -376,8 +441,8 @@ Available Sensors The following sensors are available for the USRP2/N-Series motherboards; they can be queried through the API. -* mimo_locked - clock reference locked over the MIMO cable -* ref_locked - clock reference locked (internal/external) +* **mimo_locked** - clock reference locked over the MIMO cable +* **ref_locked** - clock reference locked (internal/external) * other sensors are added when the GPSDO is enabled ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -386,13 +451,13 @@ Multiple RX channels There are two complete DDC chains in the FPGA. In the single channel case, only one chain is ever used. To receive from both channels, -the user must set the RX subdevice specification. +the user must set the **RX** subdevice specification. This hardware has only one daughterboard slot, -which has been aptly named slot "A". +which has been aptly named slot **A**. In the following example, a TVRX2 is installed. -Channel 0 is sourced from subdevice RX1, -channel 1 is sourced from subdevice RX2: +Channel 0 is sourced from subdevice **RX1**, +and channel 1 is sourced from subdevice **RX2**: :: usrp->set_rx_subdev_spec("A:RX1 A:RX2"); diff --git a/host/docs/usrp_b1xx.rst b/host/docs/usrp_b100.rst index 538d60fce..cdb853b61 100644 --- a/host/docs/usrp_b1xx.rst +++ b/host/docs/usrp_b100.rst @@ -1,14 +1,28 @@ ======================================================================== -UHD - USRP-B1XX Series Application Notes +UHD - USRP-B100 Series Application Notes ======================================================================== .. contents:: Table of Contents ------------------------------------------------------------------------ -Specify a non-standard image +Comparative features list ------------------------------------------------------------------------ -The UHD will automatically select the USRP B-Series images from the installed images package. -The image selection can be overridden with the "fpga" and "fw" device address parameters. + +* 1 transceiver card slot +* 1 RX DDC chain in FPGA +* 1 TX DUC chain in FPGA +* Timed commands in FPGA +* Timed sampling in FPGA +* External PPS reference +* External 10MHz reference +* Configurable clock rate (defaults 64 MHz) +* sc8 and sc16 sample modes + +------------------------------------------------------------------------ +Specify a Non-standard Image +------------------------------------------------------------------------ +UHD will automatically select the USRP B-Series images from the installed images package. +The image selection can be overridden with the **--fpga=** and **--fw=** device address parameters. Example device address string representations to specify non-standard images: @@ -21,15 +35,15 @@ Example device address string representations to specify non-standard images: fw=usrp_b100_fw_firmware.ihx ------------------------------------------------------------------------ -Changing the master clock rate +Changing the Master Clock Rate ------------------------------------------------------------------------ The master clock rate of the USRP embedded feeds both the FPGA DSP and the codec chip. 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 +* **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 @@ -40,7 +54,7 @@ and X4 must be populated with a 61.44 MHz oscillator. * **J15** is a three pin header, move the jumper to (pin1, pin2) * **357LB3I061M4400** is the recommended oscillator for X4 -**Note:** See instructions below to communicate the desired clock rate into the UHD. +**Note:** See instructions below to communicate the desired clock rate into UHD. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set other rates - uses internal VCO @@ -49,9 +63,9 @@ To use other clock rates, the jumper will need to be in the default position. * **J15** is a three pin header, move the jumper to (pin2, pin3) -To communicate the desired clock rate into the UHD, +To communicate the desired clock rate into UHD, specify the a special device address argument, -where the key is "master_clock_rate" and the value is a rate in Hz. +where the key is **master_clock_rate** and the value is a rate in Hz. Example: :: @@ -84,4 +98,4 @@ Available Sensors The following sensors are available; they can be queried through the API. -* ref_locked - clock reference locked (internal/external) +* **ref_locked:** clock reference locked (internal/external) diff --git a/host/docs/usrp_e1xx.rst b/host/docs/usrp_e1x0.rst index ef1e22b3a..189cbb86b 100644 --- a/host/docs/usrp_e1xx.rst +++ b/host/docs/usrp_e1x0.rst @@ -1,15 +1,30 @@ ======================================================================== -UHD - USRP-E1XX Series Application Notes +UHD - USRP-E1X0 Series Application Notes ======================================================================== .. contents:: Table of Contents ------------------------------------------------------------------------ -Specify a non-standard image +Comparative features list +------------------------------------------------------------------------ + +* 1 transceiver card slot +* 2 RX DDC chains in FPGA +* 1 TX DUC chain in FPGA +* Timed commands in FPGA +* Timed sampling in FPGA +* Internal PPS reference +* Internal 10MHz reference +* Configurable clock rate (defaults 64 MHz) +* Internal GPSDO option +* sc8 and sc16 sample modes + +------------------------------------------------------------------------ +Specify a Non-standard Image ------------------------------------------------------------------------ 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. +**--fpga=** device address parameter. Example device address string representations to specify non-standard FPGA image: @@ -19,15 +34,15 @@ image: fpga=usrp_e100_custom.bin ------------------------------------------------------------------------ -Changing the master clock rate +Changing the Master Clock Rate ------------------------------------------------------------------------ The master clock rate of the USRP-Embedded feeds both the FPGA DSP and the codec chip. 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 +* **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 @@ -35,23 +50,22 @@ Set 61.44MHz - uses external VCXO To use the 61.44MHz clock rate with the USRP-Embedded, two jumpers must be moved on the device. -* 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) +* **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). -**Note:** See instructions below to communicate the desired clock rate into the -UHD. +**Note:** See instructions below to communicate the desired clock rate UHD. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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) +* **J16** is a two pin header; move the jumper to (pin1, pin2). +* **J15** is a three pin header; move the jumper to (pin2, pin3). -To communicate the desired clock rate into the UHD, +To communicate the desired clock rate into UHD, specify the a special device address argument, -where the key is "master_clock_rate" and the value is a rate in Hz. +where the key is **master_clock_rate** and the value is a rate in Hz. Example: :: @@ -66,15 +80,15 @@ Clock Synchronization Ref Clock - 10MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The E1xx has a 10MHz TCXO which can be used to discipline the flexible clocking -by selecting REF_INT for the clock_config_t. +by selecting **REF_INT** for the **clock_config_t**. Alternately, an external 10MHz reference clock can be supplied by soldering a connector. -* Connector J10 (REF_IN) needs MCX connector WM5541-ND or similar -* Square wave will offer the best phase noise performance, but sinusoid is acceptable -* Power level: 0 to 15dBm -* Select REF_SMA in clock_config_t +* Connector **J10** (REF_IN) needs MCX connector **WM5541-ND** or similar. +* Square wave will offer the best phase noise performance, but sinusoid is acceptable. +* **Power level:** 0 to 15dBm +* Select **REF_SMA** in **clock_config_t**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,13 +97,13 @@ PPS - Pulse Per Second An exteral PPS signal for timestamp synchronization can be supplied by soldering a connector. -* Connector J13 (PPS) needs MCX connector WM5541-ND or similar -* Requires a square wave signal -* Amplitude: 3.3 to 5Vpp +* Connector **J13** (PPS) needs MCX connector **WM5541-ND** or similar. +* Requires a square wave signal. +* **Amplitude:** 3.3 to 5Vpp Test the PPS input with the following app: -* <args> are device address arguments (optional if only one USRP is on your machine) +* **<args** are device address arguments (optional if only one USRP is on your machine). :: @@ -106,7 +120,7 @@ UHD will always try to detect an installed GPSDO at runtime. There is not a special EEPROM value to burn for GPSDO detection. ------------------------------------------------------------------------ -Hardware setup notes +Hardware Setup Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +130,7 @@ The LEDs on the front panel can be useful in debugging hardware and software issues. The LEDs reveal the following about the state of the device: * **LED A:** transmitting -* **LED B:** fpga loaded +* **LED B:** PPS signal * **LED C:** receiving * **LED D:** fpga loaded * **LED E:** reference lock @@ -132,5 +146,5 @@ Available Sensors The following sensors are available; they can be queried through the API. -* ref_locked - clock reference locked (internal/external) +* **ref_locked:** clock reference locked (internal/external) * other sensors are added when the GPSDO is enabled diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index 3c9a3880a..3ba483134 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2012 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 @@ -25,12 +25,16 @@ SET(example_sources rx_samples_to_file.cpp rx_samples_to_udp.cpp rx_timed_samples.cpp + test_dboard_coercion.cpp test_messages.cpp test_pps_input.cpp + test_timed_commands.cpp + transport_hammer.cpp tx_bursts.cpp tx_samples_from_file.cpp tx_timed_samples.cpp tx_waveforms.cpp + txrx_loopback_to_file.cpp latency_test.cpp ) diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index 8f00e25de..8a000f6c3 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 <uhd/utils/thread_priority.hpp> +#include <uhd/convert.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/usrp/multi_usrp.hpp> #include <boost/program_options.hpp> @@ -40,11 +41,13 @@ unsigned long long num_seq_errors = 0; /*********************************************************************** * Benchmark RX Rate **********************************************************************/ -void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_otw){ +void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, const std::string &rx_otw){ uhd::set_thread_priority_safe(); //create a receive streamer - uhd::stream_args_t stream_args("fc32", rx_otw); //complex floats + uhd::stream_args_t stream_args(rx_cpu, rx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //print pre-test summary @@ -55,16 +58,20 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_o //setup variables and allocate buffer uhd::rx_metadata_t md; const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); - std::vector<std::complex<float> > buff(max_samps_per_packet); + std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); + std::vector<void *> buffs; + for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + buffs.push_back(&buff.front()); //same buffer for each channel bool had_an_overflow = false; uhd::time_spec_t last_time; const double rate = usrp->get_rx_rate(); - usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); + cmd.stream_now = (buffs.size() == 1); + usrp->issue_stream_cmd(cmd); while (not boost::this_thread::interruption_requested()){ - num_rx_samps += rx_stream->recv( - &buff.front(), buff.size(), md - ); + num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md); //handle the error codes switch(md.error_code){ @@ -94,11 +101,13 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_o /*********************************************************************** * Benchmark TX Rate **********************************************************************/ -void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_otw){ +void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, const std::string &tx_otw){ uhd::set_thread_priority_safe(); //create a transmit streamer - uhd::stream_args_t stream_args("fc32", tx_otw); //complex floats + uhd::stream_args_t stream_args(tx_cpu, tx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); //print pre-test summary @@ -108,17 +117,22 @@ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_o //setup variables and allocate buffer uhd::tx_metadata_t md; - md.has_time_spec = false; + md.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); - std::vector<std::complex<float> > buff(max_samps_per_packet); + std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(tx_cpu)); + std::vector<const void *> buffs; + for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + buffs.push_back(&buff.front()); //same buffer for each channel + md.has_time_spec = (buffs.size() != 1); while (not boost::this_thread::interruption_requested()){ - num_tx_samps += tx_stream->send(&buff.front(), buff.size(), md); + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md); + md.has_time_spec = false; } //send a mini EOB packet md.end_of_burst = true; - tx_stream->send("", 0, md); + tx_stream->send(buffs, 0, md); } void benchmark_tx_rate_async_helper(uhd::usrp::multi_usrp::sptr usrp){ @@ -163,6 +177,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ double duration; double rx_rate, tx_rate; std::string rx_otw, tx_otw; + std::string rx_cpu, tx_cpu; + std::string mode; //setup the program options po::options_description desc("Allowed options"); @@ -174,6 +190,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)") ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX") ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX") + ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX") + ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX") + ("mode", po::value<std::string>(&mode)->default_value("none"), "multi-channel sync mode option: none, mimo") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -201,18 +220,24 @@ 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; + if (mode == "mimo"){ + usrp->set_clock_source("mimo", 0); + usrp->set_time_source("mimo", 0); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + } + boost::thread_group thread_group; //spawn the receive test thread if (vm.count("rx_rate")){ usrp->set_rx_rate(rx_rate); - thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_otw)); + thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_otw)); } //spawn the transmit test thread if (vm.count("tx_rate")){ usrp->set_tx_rate(tx_rate); - thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_otw)); + thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_otw)); thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, usrp)); } diff --git a/host/examples/test_dboard_coercion.cpp b/host/examples/test_dboard_coercion.cpp new file mode 100644 index 000000000..5b1e9f0e2 --- /dev/null +++ b/host/examples/test_dboard_coercion.cpp @@ -0,0 +1,577 @@ +// +// Copyright 2012 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/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <iostream> +#include <complex> +#include <vector> + +namespace po = boost::program_options; + +/************************************************************************ + * Misc functions +************************************************************************/ + +std::string return_MHz_string(double freq){ + std::string nice_string = std::string(str(boost::format("%5.2f MHz") % (freq / 1e6))); + return nice_string; +} + +std::string return_USRP_config_string(uhd::usrp::multi_usrp::sptr usrp, bool test_tx, bool test_rx){ + uhd::dict<std::string, std::string> tx_info = usrp->get_usrp_tx_info(); + uhd::dict<std::string, std::string> rx_info = usrp->get_usrp_rx_info(); + std::string info_string; + std::string mboard_id, mboard_serial; + std::string tx_serial, tx_subdev_name, tx_subdev_spec; + std::string rx_serial, rx_subdev_name, rx_subdev_spec; + + mboard_id = tx_info.get("mboard_id"); + if(tx_info.get("mboard_serial") != "") mboard_serial = tx_info.get("mboard_serial"); + else mboard_serial = "no serial"; + + info_string = std::string(str(boost::format("Motherboard: %s (%s)\n") % mboard_id % mboard_serial)); + + if(test_tx){ + if(tx_info.get("tx_serial") != "") tx_serial = tx_info.get("tx_serial"); + else tx_serial = "no serial"; + tx_subdev_name = tx_info.get("tx_subdev_name"); + tx_subdev_spec = tx_info.get("tx_subdev_spec"); + + info_string += std::string(str(boost::format("TX: %s (%s, %s)") % tx_subdev_name % tx_serial % tx_subdev_spec)); + } + if(test_tx and test_rx) info_string += "\n"; + if(test_rx){ + if(rx_info.get("rx_serial") != "") rx_serial = rx_info.get("rx_serial"); + else rx_serial = "no serial"; + rx_subdev_name = rx_info.get("rx_subdev_name"); + rx_subdev_spec = rx_info.get("rx_subdev_spec"); + + info_string += std::string(str(boost::format("RX: %s (%s, %s)") % rx_subdev_name % rx_serial % rx_subdev_spec)); + } + + return info_string; +} + +/************************************************************************ + * TX Frequency/Gain Coercion +************************************************************************/ + +std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbose){ + + //Establish frequency range + + std::vector<double> freqs; + std::vector<double> xcvr_freqs; + + BOOST_FOREACH(const uhd::range_t &range, usrp->get_fe_tx_freq_range()){ + double freq_begin = range.start(); + double freq_end = range.stop(); + double freq_step; + + if(usrp->get_usrp_tx_info().get("tx_subdev_name") == "XCVR2450 TX"){ + xcvr_freqs.push_back(freq_begin); + xcvr_freqs.push_back(freq_end); + } + + if(freq_end - freq_begin > 1000e6) freq_step = 100e6; + else if(freq_end - freq_begin < 300e6) freq_step = 10e6; + else freq_step = 50e6; + + double current_freq = freq_begin; + + while(current_freq < freq_end){ + freqs.push_back(current_freq); + current_freq += freq_step; + } + if(freq_end != *freqs.end()) freqs.push_back(freq_end); + } + + std::vector<double> gains; + + if(test_gain){ + + //Establish gain range + + double gain_begin = usrp->get_tx_gain_range().start(); + if(gain_begin < 0.0) gain_begin = 0.0; + double gain_end = usrp->get_tx_gain_range().stop(); + + double current_gain = gain_begin; + while(current_gain < gain_end){ + gains.push_back(current_gain); + current_gain++; + } + if(gain_end != *gains.end()) gains.push_back(gain_end); + + } + + //Establish error-storing variables + + std::vector<double> bad_tune_freqs; + std::vector<double> no_lock_freqs; + std::vector< std::vector< double > > bad_gain_vals; + std::vector<std::string> dboard_sensor_names = usrp->get_tx_sensor_names(); + std::vector<std::string> mboard_sensor_names = usrp->get_mboard_sensor_names(); + bool has_sensor = (std::find(dboard_sensor_names.begin(), dboard_sensor_names.end(), "lo_locked")) != dboard_sensor_names.end(); + + for(std::vector<double>::iterator f = freqs.begin(); f != freqs.end(); ++f){ + + //Testing for successful frequency tune + + usrp->set_tx_freq(*f); + boost::this_thread::sleep(boost::posix_time::microseconds(long(1000))); + + double actual_freq = usrp->get_tx_freq(); + + if(*f == 0.0){ + if(floor(actual_freq + 0.5) == 0.0){ + if(verbose) std::cout << boost::format("\nTX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("\nTX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; + } + } + else{ + if((*f / actual_freq > 0.9999) and (*f / actual_freq < 1.0001)){ + if(verbose) std::cout << boost::format("\nTX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("\nTX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; + bad_tune_freqs.push_back(*f); + } + } + + //Testing for successful lock + + if(has_sensor){ + bool is_locked = false; + for(int i = 0; i < 1000; i++){ + boost::this_thread::sleep(boost::posix_time::microseconds(1000)); + if(usrp->get_tx_sensor("lo_locked",0).to_bool()){ + is_locked = true; + break; + } + } + if(is_locked){ + if(verbose) std::cout << boost::format("LO successfully locked at TX frequency %s.") % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("LO did not successfully lock at TX frequency %s.") % return_MHz_string(*f) << std::endl; + no_lock_freqs.push_back(*f); + } + } + + if(test_gain){ + + //Testing for successful gain tune + + for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ + usrp->set_tx_gain(*g); + boost::this_thread::sleep(boost::posix_time::microseconds(1000)); + + double actual_gain = usrp->get_tx_gain(); + + if(*g == 0.0){ + if(actual_gain == 0.0){ + if(verbose) std::cout << boost::format("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("TX gain set to %5.2f instead of %5.2f at TX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; + std::vector<double> bad_gain_freq; + bad_gain_freq.push_back(*f); + bad_gain_freq.push_back(*g); + bad_gain_vals.push_back(bad_gain_freq); + } + } + else{ + if((*g / actual_gain) > 0.9 and (*g / actual_gain) < 1.1){ + if(verbose) std::cout << boost::format("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("TX gain set to %5.2f instead of %5.2f at TX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; + std::vector<double> bad_gain_freq; + bad_gain_freq.push_back(*f); + bad_gain_freq.push_back(*g); + bad_gain_vals.push_back(bad_gain_freq); + } + } + } + } + } + + std::string tx_results = "TX Summary:\n"; + if(usrp->get_usrp_tx_info().get("tx_subdev_name") == "XCVR2450 TX"){ + tx_results += std::string(str(boost::format("Frequency Range: %s - %s, %s - %s\n") % return_MHz_string(xcvr_freqs.at(0)) % return_MHz_string(xcvr_freqs.at(1)) % + return_MHz_string(xcvr_freqs.at(2)) % return_MHz_string(xcvr_freqs.at(3)))); + } + else tx_results += std::string(str(boost::format("Frequency Range: %s - %s\n") % return_MHz_string(freqs.front()) % return_MHz_string(freqs.back()))); + if(test_gain) tx_results += std::string(str(boost::format("Gain Range: %5.2f - %5.2f\n") % gains.front() % gains.back())); + + if(bad_tune_freqs.empty()) tx_results += "USRP successfully tuned to all frequencies."; + else{ + tx_results += "USRP did not successfully tune to the following frequencies: "; + for(std::vector<double>::iterator i = bad_tune_freqs.begin(); i != bad_tune_freqs.end(); ++i){ + if(i != bad_tune_freqs.begin()) tx_results += ", "; + tx_results += return_MHz_string(*i); + } + } + if(has_sensor){ + + tx_results += "\n"; + if(no_lock_freqs.empty()) tx_results += "LO successfully locked at all frequencies."; + else{ + tx_results += "LO did not lock at the following frequencies: "; + for(std::vector<double>::iterator i = no_lock_freqs.begin(); i != no_lock_freqs.end(); ++i){ + if(i != no_lock_freqs.begin()) tx_results += ", "; + tx_results += return_MHz_string(*i); + } + } + } + if(test_gain){ + tx_results += "\n"; + if(bad_gain_vals.empty()) tx_results += "USRP successfully set all specified gain values at all frequencies."; + else{ + tx_results += "USRP did not successfully set gain under the following circumstances:"; + for(std::vector< std::vector<double> >::iterator i = bad_gain_vals.begin(); i != bad_gain_vals.end(); ++i){ + std::vector<double> bad_pair = *i; + double bad_freq = bad_pair.front(); + double bad_gain = bad_pair.back(); + tx_results += std::string(str(boost::format("\nFrequency: %s, Gain: %5.2f") % return_MHz_string(bad_freq) % bad_gain)); + } + } + } + + return tx_results; +} + +/************************************************************************ + * RX Frequency/Gain Coercion +************************************************************************/ + +std::string rx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbose){ + + //Establish frequency range + + std::vector<double> freqs; + std::vector<double> xcvr_freqs; + + BOOST_FOREACH(const uhd::range_t &range, usrp->get_fe_rx_freq_range()){ + double freq_begin = range.start(); + double freq_end = range.stop(); + + if(usrp->get_usrp_rx_info().get("rx_subdev_name") == "XCVR2450 RX"){ + xcvr_freqs.push_back(freq_begin); + xcvr_freqs.push_back(freq_end); + } + + double freq_step; + + if(freq_end - freq_begin > 1000e6) freq_step = 100e6; + else if(freq_end - freq_begin < 300e6) freq_step = 10e6; + else freq_step = 50e6; + + double current_freq = freq_begin; + + while(current_freq < freq_end){ + freqs.push_back(current_freq); + current_freq += freq_step; + } + } + + std::vector<double> gains; + + if(test_gain){ + + //Establish gain range + + double gain_begin = usrp->get_rx_gain_range().start(); + if(gain_begin < 0.0) gain_begin = 0.0; + double gain_end = usrp->get_rx_gain_range().stop(); + + double current_gain = gain_begin; + while(current_gain < gain_end){ + gains.push_back(current_gain); + current_gain++; + } + if(gain_end != *gains.end()) gains.push_back(gain_end); + + } + + //Establish error-storing variables + + std::vector<double> bad_tune_freqs; + std::vector<double> no_lock_freqs; + std::vector< std::vector< double > > bad_gain_vals; + std::vector<std::string> dboard_sensor_names = usrp->get_rx_sensor_names(); + std::vector<std::string> mboard_sensor_names = usrp->get_mboard_sensor_names(); + bool has_sensor = (std::find(dboard_sensor_names.begin(), dboard_sensor_names.end(), "lo_locked")) != dboard_sensor_names.end(); + + for(std::vector<double>::iterator f = freqs.begin(); f != freqs.end(); ++f){ + + //Testing for successful frequency tune + + usrp->set_rx_freq(*f); + boost::this_thread::sleep(boost::posix_time::microseconds(long(1000))); + + double actual_freq = usrp->get_rx_freq(); + + if(*f == 0.0){ + if(floor(actual_freq + 0.5) == 0.0){ + if(verbose) std::cout << boost::format("\nRX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("\nRX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; + } + } + else{ + if((*f / actual_freq > 0.9999) and (*f / actual_freq < 1.0001)){ + if(verbose) std::cout << boost::format("\nRX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("\nRX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; + bad_tune_freqs.push_back(*f); + } + } + + //Testing for successful lock + + if(has_sensor){ + bool is_locked = false; + for(int i = 0; i < 1000; i++){ + boost::this_thread::sleep(boost::posix_time::microseconds(1000)); + if(usrp->get_rx_sensor("lo_locked",0).to_bool()){ + is_locked = true; + break; + } + } + if(is_locked){ + if(verbose) std::cout << boost::format("LO successfully locked at RX frequency %s.") % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("LO did not successfully lock at RX frequency %s.") % return_MHz_string(*f) << std::endl; + no_lock_freqs.push_back(*f); + } + } + + if(test_gain){ + + //Testing for successful gain tune + + for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ + usrp->set_rx_gain(*g); + boost::this_thread::sleep(boost::posix_time::microseconds(1000)); + + double actual_gain = usrp->get_rx_gain(); + + if(*g == 0.0){ + if(actual_gain == 0.0){ + if(verbose) std::cout << boost::format("RX gain successfully set to %5.2f at RX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("RX gain set to %5.2f instead of %5.2f at RX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; + std::vector<double> bad_gain_freq; + bad_gain_freq.push_back(*f); + bad_gain_freq.push_back(*g); + bad_gain_vals.push_back(bad_gain_freq); + } + } + else{ + if((*g / actual_gain) > 0.9 and (*g / actual_gain) < 1.1){ + if(verbose) std::cout << boost::format("RX gain successfully set to %5.2f at RX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("RX gain set to %5.2f instead of %5.2f at RX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; + std::vector<double> bad_gain_freq; + bad_gain_freq.push_back(*f); + bad_gain_freq.push_back(*g); + bad_gain_vals.push_back(bad_gain_freq); + } + } + } + } + } + + std::string rx_results = "RX Summary:\n"; + if(usrp->get_usrp_rx_info().get("rx_subdev_name") == "XCVR2450 RX"){ + rx_results += std::string(str(boost::format("Frequency Range: %s - %s, %s - %s\n") % return_MHz_string(xcvr_freqs.at(0)) % return_MHz_string(xcvr_freqs.at(1)) % + return_MHz_string(xcvr_freqs.at(2)) % return_MHz_string(xcvr_freqs.at(3)))); + } + else rx_results += std::string(str(boost::format("Frequency Range: %s - %s\n") % return_MHz_string(freqs.front()) % return_MHz_string(freqs.back()))); + if(test_gain) rx_results += std::string(str(boost::format("Gain Range: %5.2f - %5.2f\n") % gains.front() % gains.back())); + + if(bad_tune_freqs.empty()) rx_results += "USRP successfully tuned to all frequencies."; + else{ + rx_results += "USRP did not successfully tune to the following frequencies: "; + for(std::vector<double>::iterator i = bad_tune_freqs.begin(); i != bad_tune_freqs.end(); ++i){ + if(i != bad_tune_freqs.begin()) rx_results += ", "; + rx_results += return_MHz_string(*i); + } + } + if(has_sensor){ + + rx_results += "\n"; + if(no_lock_freqs.empty()) rx_results += "LO successfully locked at all frequencies."; + else{ + rx_results += "LO did not successfully lock at the following frequencies: "; + for(std::vector<double>::iterator i = no_lock_freqs.begin(); i != no_lock_freqs.end(); ++i){ + if( i != no_lock_freqs.begin()) rx_results += ", "; + rx_results += return_MHz_string(*i); + } + } + } + if(test_gain){ + rx_results += "\n"; + if(bad_gain_vals.empty()) rx_results += "USRP successfully set all specified gain values at all frequencies."; + else{ + rx_results += "USRP did not successfully set gain under the following circumstances:"; + for(std::vector< std::vector<double> >::iterator i = bad_gain_vals.begin(); i != bad_gain_vals.end(); ++i){ + std::vector<double> bad_pair = *i; + double bad_freq = bad_pair.front(); + double bad_gain = bad_pair.back(); + rx_results += std::string(str(boost::format("\nFrequency: %s, Gain: %5.2f") % return_MHz_string(bad_freq) % bad_gain)); + } + } + } + + return rx_results; +} + +/************************************************************************ + * Initial Setup +************************************************************************/ + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + + //Variables + std::string args; + double gain_step; + std::string ref; + std::string tx_results; + std::string rx_results; + std::string usrp_config; + + //Set up the program options + po::options_description desc("Allowed Options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "Specify the UHD device") + ("gain_step", po::value<double>(&gain_step)->default_value(1.0), "Specify the delta between gain scans") + ("tx", "Specify to test TX frequency and gain coercion") + ("rx", "Specify to test RX frequency and gain coercion") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "Waveform type: internal, external, or mimo") + ("no_tx_gain", "Do not test TX gain") + ("no_rx_gain", "Do not test RX gain") + ("verbose", "Output every frequency and gain check instead of just final summary") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //Create a USRP device + std::cout << std::endl; + uhd::device_addrs_t device_addrs = uhd::device::find(args); + std::cout << boost::format("Creating the USRP device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << std::endl << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + usrp->set_tx_rate(1e6); + usrp->set_rx_rate(1e6); + + //Boolean variables based on command line input + bool test_tx = vm.count("tx") > 0; + bool test_rx = vm.count("rx") > 0; + bool test_tx_gain = !(vm.count("no_tx_gain") > 0) and (usrp->get_tx_gain_range().stop() > 0); + bool test_rx_gain = !(vm.count("no_rx_gain") > 0) and (usrp->get_rx_gain_range().stop() > 0); + bool verbose = vm.count("verbose") > 0; + + //Help messages, errors + if(vm.count("help") > 0){ + std::cout << "UHD Daughterboard Coercion Test\n" + "This program tests your USRP daughterboard(s) to\n" + "make sure that they can successfully tune to all\n" + "frequencies and gains in their advertised ranges.\n\n"; + std::cout << desc << std::endl; + return ~0; + } + + if(ref != "internal" and ref != "external" and ref != "mimo"){ + std::cout << desc << std::endl; + std::cout << "REF must equal internal, external, or mimo." << std::endl; + return ~0; + } + + if(vm.count("tx") + vm.count("rx") == 0){ + std::cout << desc << std::endl; + std::cout << "Specify --tx to test for TX frequency coercion\n" + "Specify --rx to test for RX frequency coercion\n"; + return ~0; + } + + if(test_rx and usrp->get_usrp_rx_info().get("rx_id") == "Basic RX (0x0001)"){ + std::cout << desc << std::endl; + std::cout << "This test does not work with the Basic RX daughterboard." << std::endl; + return ~0; + } + else if(test_rx and usrp->get_usrp_rx_info().get("rx_id") == "Unknown (0xffff)"){ + std::cout << desc << std::endl; + std::cout << "This daughterboard is unrecognized, or there is no RX daughterboard." << std::endl; + return ~0; + } + + if(test_tx and usrp->get_usrp_tx_info().get("tx_id") == "Basic TX (0x0000)"){ + std::cout << desc << std::endl; + std::cout << "This test does not work with the Basic TX daughterboard." << std::endl; + return ~0; + } + else if(test_tx and usrp->get_usrp_tx_info().get("tx_id") == "Unknown (0xffff)"){ + std::cout << desc << std::endl; + std::cout << "This daughterboard is unrecognized, or there is no TX daughterboard." << std::endl; + return ~0; + } + + //Setting clock source + usrp->set_clock_source(ref); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + + std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0); + if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { + uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); + std::cout << boost::format("Checking MIMO lock: %s ...") % mimo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(mimo_locked.to_bool()); + } + if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { + uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); + std::cout << boost::format("Checking REF lock: %s ...") % ref_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(ref_locked.to_bool()); + } + usrp_config = return_USRP_config_string(usrp, test_tx, test_rx); + if(test_tx) tx_results = tx_test(usrp, test_tx_gain, verbose); + if(test_rx) rx_results = rx_test(usrp, test_rx_gain, verbose); + + if(verbose) std::cout << std::endl; + std::cout << usrp_config << std::endl << std::endl; + if(test_tx) std::cout << tx_results << std::endl; + if(test_tx and test_rx) std::cout << std::endl; + if(test_rx) std::cout << rx_results << std::endl; + + return 0; +} diff --git a/host/examples/test_timed_commands.cpp b/host/examples/test_timed_commands.cpp new file mode 100644 index 000000000..34c83dfd6 --- /dev/null +++ b/host/examples/test_timed_commands.cpp @@ -0,0 +1,129 @@ +// +// Copyright 2012 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/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ; + 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 Test Timed Commands %s") % desc << std::endl; + return ~0; + } + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + 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; + + //check if timed commands are supported + std::cout << std::endl; + std::cout << "Testing support for timed commands on this hardware... " << std::flush; + try{ + usrp->set_command_time(uhd::time_spec_t(0.0)); + usrp->clear_command_time(); + } + catch (const std::exception &e){ + std::cout << "fail" << std::endl; + std::cerr << "Got exception: " << e.what() << std::endl; + std::cerr << "Timed commands are not supported on this hardware." << std::endl; + return ~0; + } + std::cout << "pass" << std::endl; + + //readback time really fast, time diff is small + std::cout << std::endl; + std::cout << "Perform fast readback of registers:" << std::endl; + uhd::time_spec_t total_time; + for (size_t i = 0; i < 100; i++){ + const uhd::time_spec_t t0 = usrp->get_time_now(); + const uhd::time_spec_t t1 = usrp->get_time_now(); + total_time += (t1-t0); + } + std::cout << boost::format( + "Difference between paired reads: %f us" + ) % (total_time.get_real_secs()/100*1e6) << std::endl; + + //use a timed command to start a stream at a specific time + //this is not the right way start streaming at time x, + //but it should approximate it within control RTT/2 + //setup streaming + std::cout << std::endl; + std::cout << "About to start streaming using timed command:" << std::endl; + + //create a receive streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = 100; + stream_cmd.stream_now = true; + const uhd::time_spec_t stream_time = usrp->get_time_now() + uhd::time_spec_t(0.1); + usrp->set_command_time(stream_time); + usrp->issue_stream_cmd(stream_cmd); + usrp->clear_command_time(); + + //meta-data will be filled in by recv() + uhd::rx_metadata_t md; + + //allocate buffer to receive with samples + std::vector<std::complex<float> > buff(stream_cmd.num_samps); + + const size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md); + if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ + throw std::runtime_error(str(boost::format( + "Unexpected error code 0x%x" + ) % md.error_code)); + } + std::cout << boost::format( + "Received packet: %u samples, %u full secs, %f frac secs" + ) % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl; + std::cout << boost::format( + "Stream time was: %u full secs, %f frac secs" + ) % stream_time.get_full_secs() % stream_time.get_frac_secs() << std::endl; + std::cout << boost::format( + "Difference between stream time and first packet: %f us" + ) % ((md.time_spec-stream_time).get_real_secs()/100*1e6) << std::endl; + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp new file mode 100644 index 000000000..597614050 --- /dev/null +++ b/host/examples/transport_hammer.cpp @@ -0,0 +1,340 @@ +// +// Copyright 2012 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/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +/************************************************************************ + * RX Samples + ************************************************************************/ + +void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, double rx_rate, bool rx_rand, int rx_low, int rx_high, int rx_step, bool verbose){ + uhd::set_thread_priority_safe(); + + //Set RX sample rate + std::cout << boost::format("Setting RX rate: %f Msps") % (rx_rate/1e6) << std::endl; + usrp->set_rx_rate(rx_rate); + std::cout << boost::format("Actual RX rate: %f Msps") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl; + + if(rx_rand){ + std::srand((unsigned int) time(NULL)); + + while(true){ + size_t total_num_samps = (rand() % (rx_high - rx_low)) + rx_low; + + usrp->set_time_now(uhd::time_spec_t(0.0)); + + //Create a receive streamer + uhd::stream_args_t stream_args("fc32"); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + std::cout << boost::format("About to receive %u samples.") % total_num_samps << std::endl; + + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = true; + usrp->issue_stream_cmd(stream_cmd); + + //Metadata will be filled in by recv() + uhd::rx_metadata_t md; + + //Allocate buffer to receive with samples + std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); + double timeout = 1; + + size_t num_acc_samps = 0; //Number of accumulated samples + while(num_acc_samps < total_num_samps){ + //Receive a single packet + size_t num_rx_samps = rx_stream->recv( + &buff.front(), buff.size(), md, timeout, true + ); + + //Handle the error code + if(md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT){std::cout << "timeout" << std::endl; break;} + if(md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE && md.error_code != uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ + std::cout << "Error" << std::endl; + throw std::runtime_error(str(boost::format( + "Unexpected error code 0x%x" + ) % md.error_code)); + } + num_acc_samps += num_rx_samps; + } + + if(num_acc_samps < total_num_samps) std::cerr << "Received timeout before all samples were received..." << std::endl; + else std::cout << boost::format("Successfully received %u samples.") % total_num_samps << std::endl; + } + } + else{ + for(int i = int(rx_low); i <= int(rx_high); i += rx_step){ + usrp->set_time_now(uhd::time_spec_t(0.0)); + + //Create a receive streamer + uhd::stream_args_t stream_args("fc32"); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + //Set up streaming + std::cout << boost::format ("About to receive %u samples.") % i << std::endl; + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = i; + stream_cmd.stream_now = true; + usrp->issue_stream_cmd(stream_cmd); + + //Metadata will be filled in by recv() + uhd::rx_metadata_t md; + + //Allocate buffer to receive with samples + std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); + + double timeout = 1; + + size_t num_acc_samps = 0; //Number of accumulated samples + while(int(num_acc_samps) < i){ + //Receive a single packet + size_t num_rx_samps = rx_stream->recv( + &buff.front(), buff.size(), md, timeout, true + ); + + //Handle the error code + if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; + if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE && md.error_code != uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ + throw std::runtime_error(str(boost::format( + "Unexpected error code 0x%x" + ) % md.error_code)); + } + + if(verbose) std::cout << boost::format("Received %u samples.") % num_rx_samps << std::endl; + + num_acc_samps += num_rx_samps; + + } + std::cout << boost::format("Successfully received %u samples.") % i << std::endl; + + if (int(num_acc_samps) < i) std::cerr << "Timeout received before all samples were received..." << std::endl; + + } + } +} + +/************************************************************************ + * TX Samples + ************************************************************************/ + +void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, double tx_rate, bool tx_rand, int tx_low, int tx_high, int tx_step, double tx_ampl, bool verbose){ + uhd::set_thread_priority_safe(); + + //Set the TX sample rate + std::cout << boost::format("Setting TX Rate: %f Msps...") % (tx_rate / 1e6) << std::endl; + usrp->set_tx_rate(tx_rate); + std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate()/1e6) << std::endl << std::endl; + usrp->set_time_now(uhd::time_spec_t(0.0)); + + //Create a transmit streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + + //Allocate buffer with data to send + std::vector<std::complex<float> > buff(tx_stream->get_max_num_samps(), std::complex<float>(tx_ampl, tx_ampl)); + + //Setup metadata for the first packet + uhd::tx_metadata_t md; + md.start_of_burst = false; + md.end_of_burst = false; + md.has_time_spec = false; + + if(tx_rand){ + std::srand((unsigned int) time(NULL)); + + while(true){ + size_t total_num_samps = (rand() % (tx_high - tx_low)) + tx_low; + size_t num_acc_samps = 0; + float timeout = 1; + + std::cout << boost::format("About to send %u samples.") % total_num_samps << std::endl; + + usrp->set_time_now(uhd::time_spec_t(0.0)); + + while(num_acc_samps < total_num_samps){ + size_t samps_to_send = std::min(total_num_samps - num_acc_samps, buff.size()); + + //Send a single packet + size_t num_tx_samps = tx_stream->send(&buff.front(), samps_to_send, md, timeout); + + if(num_tx_samps < samps_to_send) std::cerr << "Send timeout..." << std::endl; + + num_acc_samps += num_tx_samps; + } + + md.end_of_burst = true; + tx_stream->send("", 0, md); + + if(verbose) std::cout << std::endl; + std::cout << "Waiting for async burst ACK... " << std::flush; + uhd::async_metadata_t async_md; + bool got_async_burst_ack = false; + + //Loop through all messages for the ACK packet (may have underflow messages in queue) + while (not got_async_burst_ack and usrp->get_device()->recv_async_msg(async_md, timeout)){ + got_async_burst_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK); + } + std::cout << (got_async_burst_ack? "Success!" : "Failure...") << std::endl; + + std::cout << boost::format("Successfully sent %u samples.") % total_num_samps << std::endl; + + } + } + else{ + float timeout = 1; + + for(int i = int(tx_low); i <= int(tx_high); i += tx_step){ + + usrp->set_time_now(uhd::time_spec_t(0.0)); + + std::cout << boost::format("About to send %u samples.") % i << std::endl; + if(verbose) std::cout << std::endl; + + size_t num_acc_samps = 0; //Number of accumulated samples + size_t total_num_samps = i; + + while(num_acc_samps < total_num_samps){ + size_t samps_to_send = std::min(total_num_samps - num_acc_samps, buff.size()); + + //Send a single packet + size_t num_tx_samps = tx_stream->send( + &buff.front(), samps_to_send, md, timeout + ); + + if (num_tx_samps < samps_to_send) std::cerr << "Send timeout..." << std::endl; + + num_acc_samps += num_tx_samps; + } + + //Send a mini EOB packet + md.end_of_burst = true; + tx_stream->send("", 0, md); + + std::cout << std::endl << "Waiting for async burst ACK... " << std::flush; + uhd::async_metadata_t async_md; + bool got_async_burst_ack = false; + //Loop through all messages for the ACK packet (may have underflow messages in queue) + while (not got_async_burst_ack and usrp->get_device()->recv_async_msg(async_md, timeout)){ + got_async_burst_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK); + } + std::cout << (got_async_burst_ack? "Success!" : "Failure...") << std::endl; + + } + //Finished + std::cout << "Done!" << std::endl; + } +} + +/************************************************************************ + * Main code + dispatcher + ************************************************************************/ + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //Variables to be set by program options + std::string args; + double rx_rate; + int rx_low; + int rx_high; + int rx_step; + double tx_rate; + int tx_low; + int tx_high; + int tx_step; + double tx_ampl; + + //Set up the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "Print this help message.") + ("args", po::value<std::string>(&args)->default_value(""), "Single UHD device address args.") + ("rx_rate", po::value<double>(&rx_rate), "RX sample rate.") + ("rx_rand", "Specify to use random amounts of RX samples (between rx_low and rx_high values).") + ("rx_low", po::value<int>(&rx_low)->default_value(1), "Lowest value of RX samples.") + ("rx_high", po::value<int>(&rx_high)->default_value(10000), "Highest value of RX samples.") + ("rx_step", po::value<int>(&rx_step)->default_value(10), "Delta between number of collected RX samples.") + ("tx_rate", po::value<double>(&tx_rate), "TX sample rate.") + ("tx_rand", "Specify to use random amounts of TX samples (between tx_low and tx_high values).") + ("tx_low", po::value<int>(&tx_low)->default_value(1), "Lowest value of TX samples.") + ("tx_high", po::value<int>(&tx_high)->default_value(10000), "Highest value of TX samples.") + ("tx_step", po::value<int>(&tx_step)->default_value(10), "Delta between number of sent TX samples.") + ("tx_ampl", po::value<double>(&tx_ampl)->default_value(0.5), "TX amplitude.") + ("verbose", "Enables verbosity") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //Set verbose or RX/TX random if requested by user + bool rx_rand = vm.count("rx_rand") > 0; + bool tx_rand = vm.count("tx_rand") > 0; + bool verbose = vm.count("verbose") > 0; + + //Print the help message + + if (vm.count("help") or (vm.count("rx_rate") + vm.count("tx_rate")) == 0){ + std::cout << boost::format("UHD Transport Hammer %s") % desc << std::endl; + std::cout << + " Specify --rx_rate for a receive-only test.\n" + " Specify --tx_rate for a transmit-only test.\n" + " Specify both options for a full-duplex test.\n" + << std::endl; + return ~0; + } + + //Create a USRP device + std::cout << std::endl; + uhd::device_addrs_t device_addrs = uhd::device::find(args); + std::cout << boost::format("Creating the USRP device with: %s...") % args << std::endl; + 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; + + boost::thread_group thread_group; + + //Spawn the receive test thread + if (vm.count("rx_rate")){ + usrp->set_rx_rate(rx_rate); + thread_group.create_thread(boost::bind(&rx_hammer, usrp, rx_rate, rx_rand, rx_low, rx_high, rx_step, verbose)); + } + + //Spawn the transmit test thread + if (vm.count("tx_rate")){ + usrp->set_tx_rate(tx_rate); + thread_group.create_thread(boost::bind(&tx_hammer, usrp, tx_rate, tx_rand, tx_low, tx_high, tx_step, tx_ampl, verbose)); + } + + //Interrupt and join the threads + boost::this_thread::sleep(boost::posix_time::microseconds(long(1e6))); + thread_group.interrupt_all(); + thread_group.join_all(); + //Finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/tx_samples_from_file.cpp b/host/examples/tx_samples_from_file.cpp index ea4add403..c0812c007 100644 --- a/host/examples/tx_samples_from_file.cpp +++ b/host/examples/tx_samples_from_file.cpp @@ -30,11 +30,12 @@ namespace po = boost::program_options; template<typename samp_type> void send_from_file( uhd::usrp::multi_usrp::sptr usrp, const std::string &cpu_format, + const std::string &wire_format, const std::string &file, size_t samps_per_buff ){ //create a transmit streamer - uhd::stream_args_t stream_args(cpu_format); + uhd::stream_args_t stream_args(cpu_format, wire_format); uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); uhd::tx_metadata_t md; @@ -61,7 +62,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::set_thread_priority_safe(); //variables to be set by po - std::string args, file, type, ant, subdev, ref; + std::string args, file, type, ant, subdev, ref, wirefmt; size_t spb; double rate, freq, gain, bw; @@ -80,6 +81,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") + ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -162,9 +164,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } //send from file - if (type == "double") send_from_file<std::complex<double> >(usrp, "fc64", file, spb); - else if (type == "float") send_from_file<std::complex<float> >(usrp, "fc32", file, spb); - else if (type == "short") send_from_file<std::complex<short> >(usrp, "sc16", file, spb); + if (type == "double") send_from_file<std::complex<double> >(usrp, "fc64", wirefmt, file, spb); + else if (type == "float") send_from_file<std::complex<float> >(usrp, "fc32", wirefmt, file, spb); + else if (type == "short") send_from_file<std::complex<short> >(usrp, "sc16", wirefmt, file, spb); else throw std::runtime_error("Unknown type " + type); //finished diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 6a377fdac..3c5eecd65 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -24,6 +24,7 @@ #include <boost/math/special_functions/round.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> +#include <boost/thread.hpp> #include <iostream> #include <complex> #include <csignal> @@ -174,6 +175,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("ant")) usrp->set_tx_antenna(ant, chan); } + boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + //for the const wave, set the wave freq for small samples per period if (wave_freq == 0 and wave_type == "CONST"){ wave_freq = usrp->get_tx_rate()/2; diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp new file mode 100644 index 000000000..495c9f7e4 --- /dev/null +++ b/host/examples/txrx_loopback_to_file.cpp @@ -0,0 +1,447 @@ +// +// Copyright 2010-2012 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/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> +#include <boost/program_options.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <fstream> +#include <complex> +#include <csignal> +#include <cmath> + +namespace po = boost::program_options; + +/*********************************************************************** + * Signal handlers + **********************************************************************/ +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +/*********************************************************************** + * Waveform generators + **********************************************************************/ +static const size_t wave_table_len = 8192; + +class wave_table_class{ +public: + wave_table_class(const std::string &wave_type, const float ampl): + _wave_table(wave_table_len) + { + //compute real wave table with 1.0 amplitude + std::vector<double> real_wave_table(wave_table_len); + if (wave_type == "CONST"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 1.0; + } + else if (wave_type == "SQUARE"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; + } + else if (wave_type == "RAMP"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; + } + else if (wave_type == "SINE"){ + static const double tau = 2*std::acos(-1.0); + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = std::sin((tau*i)/wave_table_len); + } + else throw std::runtime_error("unknown waveform type: " + wave_type); + + //compute i and q pairs with 90% offset and scale to amplitude + for (size_t i = 0; i < wave_table_len; i++){ + const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; + _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); + } + } + + inline std::complex<float> operator()(const size_t index) const{ + return _wave_table[index % wave_table_len]; + } + +private: + std::vector<std::complex<float> > _wave_table; +}; + +/*********************************************************************** + * transmit_worker function + * A function to be used as a boost::thread_group thread for transmitting + **********************************************************************/ +void transmit_worker( + std::vector<std::complex<float> > buff, + wave_table_class wave_table, + uhd::tx_streamer::sptr tx_streamer, + uhd::tx_metadata_t metadata, + size_t step, + size_t index, + int num_channels +){ + std::vector<std::complex<float> *> buffs(num_channels, &buff.front()); + + //send data until the signal handler gets called + while(not stop_signal_called){ + //fill the buffer with the waveform + for (size_t n = 0; n < buff.size(); n++){ + buff[n] = wave_table(index += step); + } + + //send the entire contents of the buffer + tx_streamer->send(buffs, buff.size(), metadata); + + metadata.start_of_burst = false; + metadata.has_time_spec = false; + } + + //send a mini EOB packet + metadata.end_of_burst = true; + tx_streamer->send("", 0, metadata); +} + + +/*********************************************************************** + * recv_to_file function + **********************************************************************/ +template<typename samp_type> void recv_to_file( + uhd::usrp::multi_usrp::sptr usrp, + const std::string &cpu_format, + const std::string &wire_format, + const std::string &file, + size_t samps_per_buff, + int num_requested_samples, + float settling_time +){ + int num_total_samps = 0; + //create a receive streamer + uhd::stream_args_t stream_args(cpu_format,wire_format); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + uhd::rx_metadata_t md; + std::vector<samp_type> buff(samps_per_buff); + std::ofstream outfile(file.c_str(), std::ofstream::binary); + bool overflow_message = true; + float timeout = settling_time + 0.1; //expected settling time + padding for first recv + + //setup streaming + uhd::stream_cmd_t stream_cmd((num_requested_samples == 0)? + uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS: + uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE + ); + stream_cmd.num_samps = num_requested_samples; + stream_cmd.stream_now = false; + stream_cmd.time_spec = uhd::time_spec_t(settling_time); + usrp->issue_stream_cmd(stream_cmd); + + while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){ + size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, timeout); + timeout = 0.1; //small timeout for subsequent recv + + if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { + std::cout << boost::format("Timeout while streaming") << std::endl; + break; + } + if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ + if (overflow_message){ + overflow_message = false; + std::cerr << boost::format( + "Got an overflow indication. Please consider the following:\n" + " Your write medium must sustain a rate of %fMB/s.\n" + " Dropped samples will not be written to the file.\n" + " Please modify this example for your purposes.\n" + " This message will not appear again.\n" + ) % (usrp->get_rx_rate()*sizeof(samp_type)/1e6); + } + continue; + } + if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ + throw std::runtime_error(str(boost::format( + "Unexpected error code 0x%x" + ) % md.error_code)); + } + + num_total_samps += num_rx_samps; + + outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); + } + + outfile.close(); +} + + +/*********************************************************************** + * Main function + **********************************************************************/ +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //transmit variables to be set by po + std::string tx_args, wave_type, tx_ant, tx_subdev, ref, otw; + double tx_rate, tx_freq, tx_gain, wave_freq, tx_bw; + float ampl; + + //receive variables to be set by po + std::string rx_args, file, type, rx_ant, rx_subdev; + size_t total_num_samps, spb; + double rx_rate, rx_freq, rx_gain, rx_bw; + float settling; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("tx-args", po::value<std::string>(&tx_args)->default_value(""), "uhd transmit device address args") + ("rx-args", po::value<std::string>(&rx_args)->default_value(""), "uhd receive device address args") + ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to write binary samples to") + ("type", po::value<std::string>(&type)->default_value("short"), "sample type in file: double, float, or short") + ("nsamps", po::value<size_t>(&total_num_samps)->default_value(0), "total number of samples to receive") + ("settling", po::value<float>(&settling)->default_value(float(0.2)), "settling time (seconds) before receiving") + ("spb", po::value<size_t>(&spb)->default_value(0), "samples per buffer, 0 for default") + ("tx-rate", po::value<double>(&tx_rate), "rate of transmit outgoing samples") + ("rx-rate", po::value<double>(&rx_rate), "rate of receive incoming samples") + ("tx-freq", po::value<double>(&tx_freq), "transmit RF center frequency in Hz") + ("rx-freq", po::value<double>(&rx_freq), "receive RF center frequency in Hz") + ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of the waveform [0 to 0.7]") + ("tx-gain", po::value<double>(&tx_gain), "gain for the transmit RF chain") + ("rx-gain", po::value<double>(&rx_gain), "gain for the receive RF chain") + ("tx-ant", po::value<std::string>(&tx_ant), "daughterboard transmit antenna selection") + ("rx-ant", po::value<std::string>(&rx_ant), "daughterboard receive antenna selection") + ("tx-subdev", po::value<std::string>(&tx_subdev), "daughterboard transmit subdevice specification") + ("rx-subdev", po::value<std::string>(&rx_subdev), "daughterboard receive subdevice specification") + ("tx-bw", po::value<double>(&tx_bw), "daughterboard transmit IF filter bandwidth in Hz") + ("rx-bw", po::value<double>(&rx_bw), "daughterboard receive IF filter bandwidth in Hz") + ("wave-type", po::value<std::string>(&wave_type)->default_value("CONST"), "waveform type (CONST, SQUARE, RAMP, SINE)") + ("wave-freq", po::value<double>(&wave_freq)->default_value(0), "waveform frequency in Hz") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "clock reference (internal, external, mimo)") + ("otw", po::value<std::string>(&otw)->default_value("sc16"), "specify the over-the-wire sample mode") + ; + 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 TXRX Loopback to File %s") % desc << std::endl; + return ~0; + } + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the transmit usrp device with: %s...") % tx_args << std::endl; + uhd::usrp::multi_usrp::sptr tx_usrp = uhd::usrp::multi_usrp::make(tx_args); + std::cout << std::endl; + std::cout << boost::format("Creating the receive usrp device with: %s...") % rx_args << std::endl; + uhd::usrp::multi_usrp::sptr rx_usrp = uhd::usrp::multi_usrp::make(rx_args); + + //Lock mboard clocks + tx_usrp->set_clock_source(ref); + rx_usrp->set_clock_source(ref); + + //always select the subdevice first, the channel mapping affects the other settings + if (vm.count("tx-subdev")) tx_usrp->set_tx_subdev_spec(tx_subdev); + if (vm.count("rx-subdev")) rx_usrp->set_rx_subdev_spec(rx_subdev); + + std::cout << boost::format("Using Device: %s") % tx_usrp->get_pp_string() << std::endl; + std::cout << boost::format("Using Device: %s") % rx_usrp->get_pp_string() << std::endl; + + //set the transmit sample rate + if (not vm.count("tx-rate")){ + std::cerr << "Please specify the transmit sample rate with --tx-rate" << std::endl; + return ~0; + } + std::cout << boost::format("Setting TX Rate: %f Msps...") % (tx_rate/1e6) << std::endl; + tx_usrp->set_tx_rate(tx_rate); + std::cout << boost::format("Actual TX Rate: %f Msps...") % (tx_usrp->get_tx_rate()/1e6) << std::endl << std::endl; + + //set the receive sample rate + if (not vm.count("rx-rate")){ + std::cerr << "Please specify the sample rate with --rx-rate" << std::endl; + return ~0; + } + std::cout << boost::format("Setting RX Rate: %f Msps...") % (rx_rate/1e6) << std::endl; + rx_usrp->set_rx_rate(rx_rate); + std::cout << boost::format("Actual RX Rate: %f Msps...") % (rx_usrp->get_rx_rate()/1e6) << std::endl << std::endl; + + //set the transmit center frequency + if (not vm.count("tx-freq")){ + std::cerr << "Please specify the transmit center frequency with --tx-freq" << std::endl; + return ~0; + } + + for(size_t chan = 0; chan < tx_usrp->get_tx_num_channels(); chan++) { + std::cout << boost::format("Setting TX Freq: %f MHz...") % (tx_freq/1e6) << std::endl; + tx_usrp->set_tx_freq(tx_freq, chan); + std::cout << boost::format("Actual TX Freq: %f MHz...") % (tx_usrp->get_tx_freq(chan)/1e6) << std::endl << std::endl; + + //set the rf gain + if (vm.count("tx-gain")){ + std::cout << boost::format("Setting TX Gain: %f dB...") % tx_gain << std::endl; + tx_usrp->set_tx_gain(tx_gain, chan); + std::cout << boost::format("Actual TX Gain: %f dB...") % tx_usrp->get_tx_gain(chan) << std::endl << std::endl; + } + + //set the IF filter bandwidth + if (vm.count("tx-bw")){ + std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % tx_bw << std::endl; + tx_usrp->set_tx_bandwidth(tx_bw, chan); + std::cout << boost::format("Actual TX Bandwidth: %f MHz...") % tx_usrp->get_tx_bandwidth(chan) << std::endl << std::endl; + } + + //set the antenna + if (vm.count("tx-ant")) tx_usrp->set_tx_antenna(tx_ant, chan); + } + + //set the receive center frequency + if (not vm.count("rx-freq")){ + std::cerr << "Please specify the center frequency with --rx-freq" << std::endl; + return ~0; + } + std::cout << boost::format("Setting RX Freq: %f MHz...") % (rx_freq/1e6) << std::endl; + rx_usrp->set_rx_freq(rx_freq); + std::cout << boost::format("Actual RX Freq: %f MHz...") % (rx_usrp->get_rx_freq()/1e6) << std::endl << std::endl; + + //set the receive rf gain + if (vm.count("rx_gain")){ + std::cout << boost::format("Setting RX Gain: %f dB...") % rx_gain << std::endl; + rx_usrp->set_rx_gain(rx_gain); + std::cout << boost::format("Actual RX Gain: %f dB...") % rx_usrp->get_rx_gain() << std::endl << std::endl; + } + + //set the receive IF filter bandwidth + if (vm.count("rx_bw")){ + std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % rx_bw << std::endl; + rx_usrp->set_rx_bandwidth(rx_bw); + std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % rx_usrp->get_rx_bandwidth() << std::endl << std::endl; + } + + //set the receive antenna + if (vm.count("ant")) rx_usrp->set_rx_antenna(rx_ant); + + //for the const wave, set the wave freq for small samples per period + if (wave_freq == 0 and wave_type == "CONST"){ + wave_freq = tx_usrp->get_tx_rate()/2; + } + + //error when the waveform is not possible to generate + if (std::abs(wave_freq) > tx_usrp->get_tx_rate()/2){ + throw std::runtime_error("wave freq out of Nyquist zone"); + } + if (tx_usrp->get_tx_rate()/std::abs(wave_freq) > wave_table_len/2){ + throw std::runtime_error("wave freq too small for table"); + } + + //pre-compute the waveform values + const wave_table_class wave_table(wave_type, ampl); + const size_t step = boost::math::iround(wave_freq/tx_usrp->get_tx_rate() * wave_table_len); + size_t index = 0; + + //create a transmit streamer + //linearly map channels (index0 = channel0, index1 = channel1, ...) + uhd::stream_args_t stream_args("fc32", otw); + for (size_t chan = 0; chan < tx_usrp->get_tx_num_channels(); chan++) + stream_args.channels.push_back(chan); //linear mapping + uhd::tx_streamer::sptr tx_stream = tx_usrp->get_tx_stream(stream_args); + + //allocate a buffer which we re-use for each channel + if (spb == 0) spb = tx_stream->get_max_num_samps()*10; + std::vector<std::complex<float> > buff(spb); + int num_channels = tx_usrp->get_tx_num_channels(); + + //setup the metadata flags + uhd::tx_metadata_t md; + md.start_of_burst = true; + md.end_of_burst = false; + md.has_time_spec = true; + md.time_spec = uhd::time_spec_t(0.1); //give us 0.1 seconds to fill the tx buffers + + //Check Ref and LO Lock detect + std::vector<std::string> tx_sensor_names, rx_sensor_names; + tx_sensor_names = tx_usrp->get_tx_sensor_names(0); + if (std::find(tx_sensor_names.begin(), tx_sensor_names.end(), "lo_locked") != tx_sensor_names.end()) { + uhd::sensor_value_t lo_locked = tx_usrp->get_tx_sensor("lo_locked",0); + std::cout << boost::format("Checking TX: %s ...") % lo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(lo_locked.to_bool()); + } + rx_sensor_names = rx_usrp->get_rx_sensor_names(0); + if (std::find(rx_sensor_names.begin(), rx_sensor_names.end(), "lo_locked") != rx_sensor_names.end()) { + uhd::sensor_value_t lo_locked = rx_usrp->get_rx_sensor("lo_locked",0); + std::cout << boost::format("Checking RX: %s ...") % lo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(lo_locked.to_bool()); + } + + tx_sensor_names = tx_usrp->get_mboard_sensor_names(0); + if ((ref == "mimo") and (std::find(tx_sensor_names.begin(), tx_sensor_names.end(), "mimo_locked") != tx_sensor_names.end())) { + uhd::sensor_value_t mimo_locked = tx_usrp->get_mboard_sensor("mimo_locked",0); + std::cout << boost::format("Checking TX: %s ...") % mimo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(mimo_locked.to_bool()); + } + if ((ref == "external") and (std::find(tx_sensor_names.begin(), tx_sensor_names.end(), "ref_locked") != tx_sensor_names.end())) { + uhd::sensor_value_t ref_locked = tx_usrp->get_mboard_sensor("ref_locked",0); + std::cout << boost::format("Checking TX: %s ...") % ref_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(ref_locked.to_bool()); + } + + rx_sensor_names = rx_usrp->get_mboard_sensor_names(0); + if ((ref == "mimo") and (std::find(rx_sensor_names.begin(), rx_sensor_names.end(), "mimo_locked") != rx_sensor_names.end())) { + uhd::sensor_value_t mimo_locked = rx_usrp->get_mboard_sensor("mimo_locked",0); + std::cout << boost::format("Checking RX: %s ...") % mimo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(mimo_locked.to_bool()); + } + if ((ref == "external") and (std::find(rx_sensor_names.begin(), rx_sensor_names.end(), "ref_locked") != rx_sensor_names.end())) { + uhd::sensor_value_t ref_locked = rx_usrp->get_mboard_sensor("ref_locked",0); + std::cout << boost::format("Checking RX: %s ...") % ref_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(ref_locked.to_bool()); + } + + if (total_num_samps == 0){ + std::signal(SIGINT, &sig_int_handler); + std::cout << "Press Ctrl + C to stop streaming..." << std::endl; + } + + //reset usrp time to prepare for transmit/receive + std::cout << boost::format("Setting device timestamp to 0...") << std::endl; + tx_usrp->set_time_now(uhd::time_spec_t(0.0)); + + //start transmit worker thread + boost::thread_group transmit_thread; + transmit_thread.create_thread(boost::bind(&transmit_worker, buff, wave_table, tx_stream, md, step, index, num_channels)); + + //recv to file + if (type == "double") recv_to_file<std::complex<double> >(rx_usrp, "fc64", otw, file, spb, total_num_samps, settling); + else if (type == "float") recv_to_file<std::complex<float> >(rx_usrp, "fc32", otw, file, spb, total_num_samps, settling); + else if (type == "short") recv_to_file<std::complex<short> >(rx_usrp, "sc16", otw, file, spb, total_num_samps, settling); + else { + //clean up transmit worker + stop_signal_called = true; + transmit_thread.join_all(); + throw std::runtime_error("Unknown type " + type); + } + + //clean up transmit worker + stop_signal_called = true; + transmit_thread.join_all(); + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + return 0; +} diff --git a/host/include/uhd/convert.hpp b/host/include/uhd/convert.hpp index f906ff0e9..c6b005867 100644 --- a/host/include/uhd/convert.hpp +++ b/host/include/uhd/convert.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -80,9 +80,13 @@ namespace uhd{ namespace convert{ /*! * Get a converter factory function. * \param id identify the conversion + * \param prio the desired prio or -1 for best * \return the converter factory function */ - UHD_API function_type get_converter(const id_type &id); + UHD_API function_type get_converter( + const id_type &id, + const priority_type prio = -1 + ); /*! * Register the size of a particular item. diff --git a/host/include/uhd/stream.hpp b/host/include/uhd/stream.hpp index 1fb846955..c8ab303ad 100644 --- a/host/include/uhd/stream.hpp +++ b/host/include/uhd/stream.hpp @@ -53,9 +53,9 @@ struct UHD_API stream_args_t{ * - fc64 - complex<double> * - fc32 - complex<float> * - sc16 - complex<int16_t> + * - sc8 - complex<int8_t> * * The following are not implemented, but are listed to demonstrate naming convention: - * - sc8 - complex<int8_t> * - f32 - float * - f64 - double * - s16 - int16_t diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index f80c738aa..1dc0e8e26 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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,23 +22,14 @@ #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <boost/intrusive_ptr.hpp> +#include <boost/detail/atomic_count.hpp> namespace uhd{ namespace transport{ - //! Create smart pointer to a reusable managed buffer - template <typename T> UHD_INLINE boost::intrusive_ptr<T> make_managed_buffer(T *p){ - p->_ref_count = 1; //reset the count to 1 reference - return boost::intrusive_ptr<T>(p, false); - } - - /*! - * A managed receive buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after reading. - */ - class UHD_API managed_recv_buffer{ + //! Simple managed buffer with release interface + class UHD_API managed_buffer{ public: - typedef boost::intrusive_ptr<managed_recv_buffer> sptr; + managed_buffer(void):_ref_count(0){} /*! * Signal to the transport that we are done with the buffer. @@ -48,84 +39,73 @@ namespace uhd{ namespace transport{ virtual void release(void) = 0; /*! + * Use commit() to re-write the length (for use with send buffers). + * \param num_bytes the number of bytes written into the buffer + */ + UHD_INLINE void commit(size_t num_bytes){ + _length = num_bytes; + } + + /*! * Get a pointer to the underlying buffer. * \return a pointer into memory */ - template <class T> inline T cast(void) const{ - return static_cast<T>(this->get_buff()); + template <class T> UHD_INLINE T cast(void) const{ + return static_cast<T>(_buffer); } /*! * Get the size of the underlying buffer. * \return the number of bytes */ - inline size_t size(void) const{ - return this->get_size(); + UHD_INLINE size_t size(void) const{ + return _length; } - private: - virtual const void *get_buff(void) const = 0; - virtual size_t get_size(void) const = 0; + //! Create smart pointer to a reusable managed buffer + template <typename T> UHD_INLINE boost::intrusive_ptr<T> make( + T *p, void *buffer, size_t length + ){ + _buffer = buffer; + _length = length; + return boost::intrusive_ptr<T>(p); + } + + boost::detail::atomic_count _ref_count; - public: int _ref_count; + protected: + void *_buffer; + size_t _length; }; - UHD_INLINE void intrusive_ptr_add_ref(managed_recv_buffer *p){ + UHD_INLINE void intrusive_ptr_add_ref(managed_buffer *p){ ++(p->_ref_count); } - UHD_INLINE void intrusive_ptr_release(managed_recv_buffer *p){ + UHD_INLINE void intrusive_ptr_release(managed_buffer *p){ if (--(p->_ref_count) == 0) p->release(); } /*! + * A managed receive buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after reading. + */ + class UHD_API managed_recv_buffer : public managed_buffer{ + public: + typedef boost::intrusive_ptr<managed_recv_buffer> sptr; + }; + + /*! * A managed send buffer: * Contains a reference to transport-managed memory, * and a method to commit the memory after writing. */ - class UHD_API managed_send_buffer{ + class UHD_API managed_send_buffer : public managed_buffer{ public: typedef boost::intrusive_ptr<managed_send_buffer> sptr; - - /*! - * Signal to the transport that we are done with the buffer. - * This should be called to commit the write to the transport object. - * After calling, the referenced memory should be considered invalid. - * \param num_bytes the number of bytes written into the buffer - */ - virtual void commit(size_t num_bytes) = 0; - - /*! - * Get a pointer to the underlying buffer. - * \return a pointer into memory - */ - template <class T> inline T cast(void) const{ - return static_cast<T>(this->get_buff()); - } - - /*! - * Get the size of the underlying buffer. - * \return the number of bytes - */ - inline size_t size(void) const{ - return this->get_size(); - } - - private: - virtual void *get_buff(void) const = 0; - virtual size_t get_size(void) const = 0; - - public: int _ref_count; }; - UHD_INLINE void intrusive_ptr_add_ref(managed_send_buffer *p){ - ++(p->_ref_count); - } - - UHD_INLINE void intrusive_ptr_release(managed_send_buffer *p){ - if (--(p->_ref_count) == 0) p->commit(0); - } - /*! * A zero-copy interface for transport objects. * Provides a way to get send and receive buffers diff --git a/host/include/uhd/types/ranges.hpp b/host/include/uhd/types/ranges.hpp index f0d0e1c0b..ac632df93 100644 --- a/host/include/uhd/types/ranges.hpp +++ b/host/include/uhd/types/ranges.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -19,7 +19,6 @@ #define INCLUDED_UHD_TYPES_RANGES_HPP #include <uhd/config.hpp> -#include <uhd/utils/pimpl.hpp> #include <string> #include <vector> @@ -60,7 +59,7 @@ namespace uhd{ //! Convert this range to a printable string const std::string to_pp_string(void) const; - private: UHD_PIMPL_DECL(impl) _impl; + private: double _start, _stop, _step; }; /*! diff --git a/host/include/uhd/usrp/mboard_eeprom.hpp b/host/include/uhd/usrp/mboard_eeprom.hpp index c47f894be..f064e9956 100644 --- a/host/include/uhd/usrp/mboard_eeprom.hpp +++ b/host/include/uhd/usrp/mboard_eeprom.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2012 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 @@ -34,30 +34,22 @@ namespace uhd{ namespace usrp{ */ struct UHD_API mboard_eeprom_t : uhd::dict<std::string, std::string>{ - //! Possible EEPROM maps types - enum map_type{ - MAP_N100, - MAP_B000, - MAP_B100, - MAP_E100 - }; - //! Make a new empty mboard eeprom mboard_eeprom_t(void); /*! * Make a new mboard EEPROM handler. * \param iface the interface to i2c - * \param map the map type enum + * \param which which EEPROM map to use */ - mboard_eeprom_t(i2c_iface &iface, map_type map); + mboard_eeprom_t(i2c_iface &iface, const std::string &which); /*! * Write the contents of this object to the EEPROM. * \param iface the interface to i2c - * \param map the map type enum + * \param which which EEPROM map to use */ - void commit(i2c_iface &iface, map_type map) const; + void commit(i2c_iface &iface, const std::string &which) const; }; diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 88affa40c..2e83823ba 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -25,6 +25,7 @@ #define UHD_USRP_MULTI_USRP_COMMAND_TIME_API #define UHD_USRP_MULTI_USRP_BW_RANGE_API #define UHD_USRP_MULTI_USRP_USER_REGS_API +#define UHD_USRP_MULTI_USRP_GET_USRP_INFO_API #include <uhd/config.hpp> #include <uhd/device.hpp> @@ -127,6 +128,24 @@ public: return this->get_device()->get_tx_stream(args); } + /*! + * Returns identifying information about this USRP's configuration. + * Returns motherboard ID, name, and serial. + * Returns daughterboard RX ID, subdev name and spec, serial, and antenna. + * \param chan channel index 0 to N-1 + * \return RX info + */ + virtual dict<std::string, std::string> get_usrp_rx_info(size_t chan = 0) = 0; + + /*! + * Returns identifying information about this USRP's configuration. + * Returns motherboard ID, name, and serial. + * Returns daughterboard TX ID, subdev name and spec, serial, and antenna. + * \param chan channel index 0 to N-1 + * \return TX info + */ + virtual dict<std::string, std::string> get_usrp_tx_info(size_t chan = 0) = 0; + /******************************************************************* * Mboard methods ******************************************************************/ @@ -428,6 +447,13 @@ public: virtual freq_range_t get_rx_freq_range(size_t chan = 0) = 0; /*! + * Get the center frequency range of the RF frontend. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_fe_rx_freq_range(size_t chan = 0) = 0; + + /*! * Set the RX gain value for the specified gain element. * For an empty name, distribute across all gain elements. * \param gain the gain in dB @@ -674,6 +700,13 @@ public: virtual freq_range_t get_tx_freq_range(size_t chan = 0) = 0; /*! + * Get the center frequency range of the TX frontend. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_fe_tx_freq_range(size_t chan = 0) = 0; + + /*! * Set the TX gain value for the specified gain element. * For an empty name, distribute across all gain elements. * \param gain the gain in dB diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index 5a434fd9a..de91993fe 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2012 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 @@ -19,6 +19,7 @@ INSTALL(FILES algorithm.hpp assert_has.hpp assert_has.ipp + atomic.hpp byteswap.hpp byteswap.ipp csv.hpp diff --git a/host/include/uhd/utils/atomic.hpp b/host/include/uhd/utils/atomic.hpp new file mode 100644 index 000000000..dc1790c3f --- /dev/null +++ b/host/include/uhd/utils/atomic.hpp @@ -0,0 +1,150 @@ +// +// Copyright 2012 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 INCLUDED_UHD_UTILS_ATOMIC_HPP +#define INCLUDED_UHD_UTILS_ATOMIC_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/thread/thread.hpp> +#include <boost/interprocess/detail/atomic.hpp> + +#include <boost/version.hpp> +#if BOOST_VERSION >= 104800 +# define BOOST_IPC_DETAIL boost::interprocess::ipcdetail +#else +# define BOOST_IPC_DETAIL boost::interprocess::detail +#endif + +namespace uhd{ + + //! A 32-bit integer that can be atomically accessed + class UHD_API atomic_uint32_t{ + public: + + //! Create a new atomic 32-bit integer, initialized to zero + UHD_INLINE atomic_uint32_t(void){ + this->write(0); + } + + //! Compare with cmp, swap with newval if same, return old value + UHD_INLINE boost::uint32_t cas(boost::uint32_t newval, boost::uint32_t cmp){ + return BOOST_IPC_DETAIL::atomic_cas32(&_num, newval, cmp); + } + + //! Sets the atomic integer to a new value + UHD_INLINE void write(const boost::uint32_t newval){ + BOOST_IPC_DETAIL::atomic_write32(&_num, newval); + } + + //! Gets the current value of the atomic integer + UHD_INLINE boost::uint32_t read(void){ + return BOOST_IPC_DETAIL::atomic_read32(&_num); + } + + //! Increment by 1 and return the old value + UHD_INLINE boost::uint32_t inc(void){ + return BOOST_IPC_DETAIL::atomic_inc32(&_num); + } + + //! Decrement by 1 and return the old value + UHD_INLINE boost::uint32_t dec(void){ + return BOOST_IPC_DETAIL::atomic_dec32(&_num); + } + + private: volatile boost::uint32_t _num; + }; + + /*! + * A reusable barrier to sync multiple threads. + * All threads spin on wait() until count is reset. + */ + class UHD_API reusable_barrier{ + public: + + //! Resize the barrier for N threads + void resize(const size_t size){ + _size = size; + _count.write(size); + } + + //! Wait on the barrier condition + UHD_INLINE void wait(void){ + _count.dec(); + _count.cas(_size, 0); + while (_count.read() != _size){ + boost::this_thread::interruption_point(); + boost::this_thread::yield(); + } + } + + private: + size_t _size; + atomic_uint32_t _count; + }; + + /*! + * Spin-wait on a condition with a timeout. + * \param cond an atomic variable to compare + * \param value compare to atomic for true/false + * \param timeout the timeout in seconds + * \return true for cond == value, false for timeout + */ + UHD_INLINE bool spin_wait_with_timeout( + atomic_uint32_t &cond, + boost::uint32_t value, + const double timeout + ){ + if (cond.read() == value) return true; + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(timeout); + while (cond.read() != value){ + if (time_spec_t::get_system_time() > exit_time) return false; + boost::this_thread::interruption_point(); + boost::this_thread::yield(); + } + return true; + } + + /*! + * Claimer class to provide synchronization for multi-thread access. + * Claiming enables buffer classes to be used with a buffer queue. + */ + class simple_claimer{ + public: + simple_claimer(void){ + this->release(); + } + + UHD_INLINE void release(void){ + _locked.write(0); + } + + UHD_INLINE bool claim_with_wait(const double timeout){ + if (spin_wait_with_timeout(_locked, 0, timeout)){ + _locked.write(1); + return true; + } + return false; + } + + private: + atomic_uint32_t _locked; + }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_ATOMIC_HPP */ diff --git a/host/include/uhd/utils/images.hpp b/host/include/uhd/utils/images.hpp index 8b5a1eedd..a0934fb08 100644 --- a/host/include/uhd/utils/images.hpp +++ b/host/include/uhd/utils/images.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,2012 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 @@ -33,6 +33,21 @@ namespace uhd{ */ UHD_API std::string find_image_path(const std::string &image_name); + /*! + * Search for the location of the UHD Images Downloader script. + * \return the full system path to uhd_images_downloader.py + */ + + UHD_API std::string find_images_downloader(void); + + /*! + * Return the error string for recommending using the UHD Images Downloader. + * String depends on OS. + * \return the message suggesting the use of uhd_images_downloader.py + */ + + UHD_API std::string print_images_error(void); + } //namespace uhd #endif /* INCLUDED_UHD_UTILS_IMAGES_HPP */ diff --git a/host/include/uhd/utils/paths.hpp b/host/include/uhd/utils/paths.hpp index 2261f1b77..f5a40b2c9 100644 --- a/host/include/uhd/utils/paths.hpp +++ b/host/include/uhd/utils/paths.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -29,6 +29,9 @@ namespace uhd{ //! Get a string representing the system's appdata directory UHD_API std::string get_app_path(void); + //! Get a string representing the system's pkg data directory + UHD_API std::string get_pkg_data_path(void); + } //namespace uhd #endif /* INCLUDED_UHD_UTILS_PATHS_HPP */ diff --git a/host/include/uhd/version.hpp b/host/include/uhd/version.hpp index 9a7226323..ab693f2b0 100644 --- a/host/include/uhd/version.hpp +++ b/host/include/uhd/version.hpp @@ -27,7 +27,7 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "3.4.0-0" +#define UHD_VERSION_ABI_STRING "3.4.0-3" namespace uhd{ diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index c42a0a434..0d9d0983f 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -71,10 +71,14 @@ UNSET(CMAKE_REQUIRED_FLAGS) IF(HAVE_EMMINTRIN_H) SET(convert_with_sse2_sources - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_with_sse2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_with_sse2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_to_sc8_with_sse2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_to_sc8_with_sse2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc16_to_fc64.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc16_to_fc32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc8_to_fc64.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc8_to_fc32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc64_to_sc16.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc32_to_sc16.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc64_to_sc8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc32_to_sc8.cpp ) SET_SOURCE_FILES_PROPERTIES( ${convert_with_sse2_sources} @@ -117,4 +121,5 @@ LIBUHD_PYTHON_GEN_SOURCE( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_tables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/convert_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/convert_item32.cpp ) diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index 7626e4d87..933978a8f 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -53,12 +53,12 @@ static const int PRIORITY_EMPTY = -1; #ifdef __ARM_NEON__ static const int PRIORITY_LIBORC = 3; -static const int PRIORITY_SIMD = 1; //neon conversions could be implemented better, orc wins -static const int PRIORITY_TABLE = 2; //tables require large cache, so they are slower on arm +static const int PRIORITY_SIMD = 2; //neon conversions could be implemented better, orc wins +static const int PRIORITY_TABLE = 1; //tables require large cache, so they are slower on arm #else -static const int PRIORITY_LIBORC = 1; +static const int PRIORITY_LIBORC = 2; static const int PRIORITY_SIMD = 3; -static const int PRIORITY_TABLE = 2; +static const int PRIORITY_TABLE = 1; #endif /*********************************************************************** @@ -77,123 +77,92 @@ typedef boost::int8_t s8_t; typedef boost::uint32_t item32_t; -/*********************************************************************** - * Convert complex short buffer to items32 sc16 - **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32_sc16(sc16_t num, double){ - boost::uint16_t real = num.real(); - boost::uint16_t imag = num.imag(); - return (item32_t(real) << 16) | (item32_t(imag) << 0); -} +typedef item32_t (*xtox_t)(item32_t); /*********************************************************************** - * Convert items32 sc16 buffer to complex short + * Convert xx to items32 sc16 buffer **********************************************************************/ -static UHD_INLINE sc16_t item32_sc16_to_sc16(item32_t item, double){ - return sc16_t( - boost::int16_t(item >> 16), - boost::int16_t(item >> 0) - ); -} - -/*********************************************************************** - * Convert complex float buffer to items32 sc16 - **********************************************************************/ -static UHD_INLINE item32_t fc32_to_item32_sc16(fc32_t num, double scale_factor){ +template <typename T> UHD_INLINE item32_t xx_to_item32_sc16_x1( + const std::complex<T> &num, const double scale_factor +){ boost::uint16_t real = boost::int16_t(num.real()*float(scale_factor)); boost::uint16_t imag = boost::int16_t(num.imag()*float(scale_factor)); return (item32_t(real) << 16) | (item32_t(imag) << 0); } -/*********************************************************************** - * Convert items32 sc16 buffer to complex float - **********************************************************************/ -static UHD_INLINE fc32_t item32_sc16_to_fc32(item32_t item, double scale_factor){ - return fc32_t( - float(boost::int16_t(item >> 16)*float(scale_factor)), - float(boost::int16_t(item >> 0)*float(scale_factor)) - ); -} - -/*********************************************************************** - * Convert complex double buffer to items32 sc16 - **********************************************************************/ -static UHD_INLINE item32_t fc64_to_item32_sc16(fc64_t num, double scale_factor){ - boost::uint16_t real = boost::int16_t(num.real()*scale_factor); - boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor); +template <> UHD_INLINE item32_t xx_to_item32_sc16_x1( + const sc16_t &num, const double +){ + boost::uint16_t real = boost::int16_t(num.real()); + boost::uint16_t imag = boost::int16_t(num.imag()); return (item32_t(real) << 16) | (item32_t(imag) << 0); } -/*********************************************************************** - * Convert items32 sc16 buffer to complex double - **********************************************************************/ -static UHD_INLINE fc64_t item32_sc16_to_fc64(item32_t item, double scale_factor){ - return fc64_t( - float(boost::int16_t(item >> 16)*scale_factor), - float(boost::int16_t(item >> 0)*scale_factor) - ); +template <xtox_t to_wire, typename T> +UHD_INLINE void xx_to_item32_sc16( + const std::complex<T> *input, + item32_t *output, + const size_t nsamps, + const double scale_factor +){ + for (size_t i = 0; i < nsamps; i++){ + const item32_t item = xx_to_item32_sc16_x1(input[i], scale_factor); + output[i] = to_wire(item); + } } /*********************************************************************** - * Convert items32 sc8 buffer to complex char + * Convert items32 sc16 buffer to xx **********************************************************************/ -static UHD_INLINE void item32_sc8_to_sc8(item32_t item, sc8_t &out0, sc8_t &out1, double){ - out0 = sc8_t( - boost::int8_t(item >> 8), - boost::int8_t(item >> 0) - ); - out1 = sc8_t( - boost::int8_t(item >> 24), - boost::int8_t(item >> 16) +template <typename T> UHD_INLINE std::complex<T> item32_sc16_x1_to_xx( + const item32_t item, const double scale_factor +){ + return std::complex<T>( + T(boost::int16_t(item >> 16)*float(scale_factor)), + T(boost::int16_t(item >> 0)*float(scale_factor)) ); } -/*********************************************************************** - * Convert items32 sc8 buffer to complex short - **********************************************************************/ -static UHD_INLINE void item32_sc8_to_sc16(item32_t item, sc16_t &out0, sc16_t &out1, double){ - out0 = sc16_t( - boost::int8_t(item >> 8), - boost::int8_t(item >> 0) - ); - out1 = sc16_t( - boost::int8_t(item >> 24), - boost::int8_t(item >> 16) +template <> UHD_INLINE sc16_t item32_sc16_x1_to_xx( + const item32_t item, const double +){ + return sc16_t( + boost::int16_t(item >> 16), boost::int16_t(item >> 0) ); } -/*********************************************************************** - * Convert items32 sc8 buffer to complex float - **********************************************************************/ -static UHD_INLINE void item32_sc8_to_fc32(item32_t item, fc32_t &out0, fc32_t &out1, double scale_factor){ - out0 = fc32_t( - float(boost::int8_t(item >> 8)*float(scale_factor)), - float(boost::int8_t(item >> 0)*float(scale_factor)) - ); - out1 = fc32_t( - float(boost::int8_t(item >> 24)*float(scale_factor)), - float(boost::int8_t(item >> 16)*float(scale_factor)) - ); +template <xtox_t to_host, typename T> +UHD_INLINE void item32_sc16_to_xx( + const item32_t *input, + std::complex<T> *output, + const size_t nsamps, + const double scale_factor +){ + for (size_t i = 0; i < nsamps; i++){ + const item32_t item_i = to_host(input[i]); + output[i] = item32_sc16_x1_to_xx<T>(item_i, scale_factor); + } } /*********************************************************************** - * Convert items32 sc8 buffer to complex double + * Convert xx to items32 sc8 buffer **********************************************************************/ -static UHD_INLINE void item32_sc8_to_fc64(item32_t item, fc64_t &out0, fc64_t &out1, double scale_factor){ - out0 = fc64_t( - float(boost::int8_t(item >> 8)*scale_factor), - float(boost::int8_t(item >> 0)*scale_factor) - ); - out1 = fc64_t( - float(boost::int8_t(item >> 24)*scale_factor), - float(boost::int8_t(item >> 16)*scale_factor) - ); +template <typename T> UHD_INLINE item32_t xx_to_item32_sc8_x1( + const std::complex<T> &in0, const std::complex<T> &in1, const double scale_factor +){ + boost::uint8_t real0 = boost::int8_t(in0.real()*float(scale_factor)); + boost::uint8_t imag0 = boost::int8_t(in0.imag()*float(scale_factor)); + boost::uint8_t real1 = boost::int8_t(in1.real()*float(scale_factor)); + boost::uint8_t imag1 = boost::int8_t(in1.imag()*float(scale_factor)); + return + (item32_t(real0) << 8) | (item32_t(imag0) << 0) | + (item32_t(real1) << 24) | (item32_t(imag1) << 16) + ; } -/*********************************************************************** - * Convert complex char to items32 sc8 buffer - **********************************************************************/ -static UHD_INLINE item32_t sc8_to_item32_sc8(sc8_t in0, sc8_t in1, double){ +template <> UHD_INLINE item32_t xx_to_item32_sc8_x1( + const sc16_t &in0, const sc16_t &in1, const double +){ boost::uint8_t real0 = boost::int8_t(in0.real()); boost::uint8_t imag0 = boost::int8_t(in0.imag()); boost::uint8_t real1 = boost::int8_t(in1.real()); @@ -204,10 +173,9 @@ static UHD_INLINE item32_t sc8_to_item32_sc8(sc8_t in0, sc8_t in1, double){ ; } -/*********************************************************************** - * Convert complex short to items32 sc8 buffer - **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32_sc8(sc16_t in0, sc16_t in1, double){ +template <> UHD_INLINE item32_t xx_to_item32_sc8_x1( + const sc8_t &in0, const sc8_t &in1, const double +){ boost::uint8_t real0 = boost::int8_t(in0.real()); boost::uint8_t imag0 = boost::int8_t(in0.imag()); boost::uint8_t real1 = boost::int8_t(in1.real()); @@ -218,32 +186,94 @@ static UHD_INLINE item32_t sc16_to_item32_sc8(sc16_t in0, sc16_t in1, double){ ; } -/*********************************************************************** - * Convert complex float to items32 sc8 buffer - **********************************************************************/ -static UHD_INLINE item32_t fc32_to_item32_sc8(fc32_t in0, fc32_t in1, double scale_factor){ - boost::uint8_t real0 = boost::int8_t(in0.real()*float(scale_factor)); - boost::uint8_t imag0 = boost::int8_t(in0.imag()*float(scale_factor)); - boost::uint8_t real1 = boost::int8_t(in1.real()*float(scale_factor)); - boost::uint8_t imag1 = boost::int8_t(in1.imag()*float(scale_factor)); - return - (item32_t(real0) << 8) | (item32_t(imag0) << 0) | - (item32_t(real1) << 24) | (item32_t(imag1) << 16) - ; +template <xtox_t to_wire, typename T> +UHD_INLINE void xx_to_item32_sc8( + const std::complex<T> *input, + item32_t *output, + const size_t nsamps, + const double scale_factor +){ + const size_t num_pairs = nsamps/2; + for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ + const item32_t item = xx_to_item32_sc8_x1(input[j], input[j+1], scale_factor); + output[i] = to_wire(item); + } + + if (nsamps != num_pairs*2){ + const item32_t item = xx_to_item32_sc8_x1(input[nsamps-1], std::complex<T>(0), scale_factor); + output[num_pairs] = to_wire(item); + } } /*********************************************************************** - * Convert complex double to items32 sc8 buffer + * Convert items32 sc8 buffer to xx **********************************************************************/ -static UHD_INLINE item32_t fc64_to_item32_sc8(fc64_t in0, fc64_t in1, double scale_factor){ - boost::uint8_t real0 = boost::int8_t(in0.real()*(scale_factor)); - boost::uint8_t imag0 = boost::int8_t(in0.imag()*(scale_factor)); - boost::uint8_t real1 = boost::int8_t(in1.real()*(scale_factor)); - boost::uint8_t imag1 = boost::int8_t(in1.imag()*(scale_factor)); - return - (item32_t(real0) << 8) | (item32_t(imag0) << 0) | - (item32_t(real1) << 24) | (item32_t(imag1) << 16) - ; +template <typename T> UHD_INLINE void item32_sc8_x1_to_xx( + const item32_t item, std::complex<T> &out0, std::complex<T> &out1, const double scale_factor +){ + out0 = std::complex<T>( + T(boost::int8_t(item >> 8)*float(scale_factor)), + T(boost::int8_t(item >> 0)*float(scale_factor)) + ); + out1 = std::complex<T>( + T(boost::int8_t(item >> 24)*float(scale_factor)), + T(boost::int8_t(item >> 16)*float(scale_factor)) + ); +} + +template <> UHD_INLINE void item32_sc8_x1_to_xx( + const item32_t item, sc16_t &out0, sc16_t &out1, const double +){ + out0 = sc16_t( + boost::int16_t(boost::int8_t(item >> 8)), + boost::int16_t(boost::int8_t(item >> 0)) + ); + out1 = sc16_t( + boost::int16_t(boost::int8_t(item >> 24)), + boost::int16_t(boost::int8_t(item >> 16)) + ); +} + +template <> UHD_INLINE void item32_sc8_x1_to_xx( + const item32_t item, sc8_t &out0, sc8_t &out1, const double +){ + out0 = sc8_t( + boost::int8_t(boost::int8_t(item >> 8)), + boost::int8_t(boost::int8_t(item >> 0)) + ); + out1 = sc8_t( + boost::int8_t(boost::int8_t(item >> 24)), + boost::int8_t(boost::int8_t(item >> 16)) + ); +} + +template <xtox_t to_host, typename T> +UHD_INLINE void item32_sc8_to_xx( + const item32_t *input, + std::complex<T> *output, + const size_t nsamps, + const double scale_factor +){ + input = reinterpret_cast<const item32_t *>(size_t(input) & ~0x3); + std::complex<T> dummy; + size_t num_samps = nsamps; + + if ((size_t(input) & 0x3) != 0){ + const item32_t item0 = to_host(*input++); + item32_sc8_x1_to_xx(item0, dummy, *output++, scale_factor); + num_samps--; + } + + const size_t num_pairs = num_samps/2; + for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ + const item32_t item_i = to_host(input[i]); + item32_sc8_x1_to_xx(item_i, output[j], output[j+1], scale_factor); + } + + if (num_samps != num_pairs*2){ + const item32_t item_n = to_host(input[num_pairs]); + item32_sc8_x1_to_xx(item_n, output[num_samps-1], dummy, scale_factor); + } } #endif /* INCLUDED_LIBUHD_CONVERT_COMMON_HPP */ diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index 12ad54486..dc7f8f9dc 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -22,6 +22,7 @@ #include <uhd/exception.hpp> #include <boost/cstdint.hpp> #include <boost/format.hpp> +#include <boost/foreach.hpp> #include <complex> using namespace uhd; @@ -51,17 +52,9 @@ std::string convert::id_type::to_pp_string(void) const{ } /*********************************************************************** - * Define types for the function tables - **********************************************************************/ -struct fcn_table_entry_type{ - convert::priority_type prio; - convert::function_type fcn; -}; - -/*********************************************************************** * Setup the table registry **********************************************************************/ -typedef uhd::dict<convert::id_type, fcn_table_entry_type> fcn_table_type; +typedef uhd::dict<convert::id_type, uhd::dict<convert::priority_type, convert::function_type> > fcn_table_type; UHD_SINGLETON_FCN(fcn_table_type, get_table); /*********************************************************************** @@ -72,14 +65,7 @@ void uhd::convert::register_converter( const function_type &fcn, const priority_type prio ){ - //get a reference to the function table - fcn_table_type &table = get_table(); - - //register the function if higher priority - if (not table.has_key(id) or table[id].prio < prio){ - table[id].fcn = fcn; - table[id].prio = prio; - } + get_table()[id][prio] = fcn; //----------------------------------------------------------------// UHD_LOGV(always) << "register_converter: " << id.to_pp_string() << std::endl @@ -92,9 +78,26 @@ void uhd::convert::register_converter( /*********************************************************************** * The converter functions **********************************************************************/ -convert::function_type convert::get_converter(const id_type &id){ - if (get_table().has_key(id)) return get_table()[id].fcn; - throw uhd::key_error("Cannot find a conversion routine for " + id.to_pp_string()); +convert::function_type convert::get_converter( + const id_type &id, + const priority_type prio +){ + if (not get_table().has_key(id)) throw uhd::key_error( + "Cannot find a conversion routine for " + id.to_pp_string()); + + //find a matching priority + priority_type best_prio = -1; + BOOST_FOREACH(priority_type prio_i, get_table()[id].keys()){ + if (prio_i == prio) return get_table()[id][prio]; + best_prio = std::max(best_prio, prio_i); + } + + //wanted a specific prio, didnt find + if (prio != -1) throw uhd::key_error( + "Cannot find a conversion routine [with prio] for " + id.to_pp_string()); + + //otherwise, return best prio + return get_table()[id][best_prio]; } /*********************************************************************** diff --git a/host/lib/convert/convert_item32.cpp b/host/lib/convert/convert_item32.cpp new file mode 100644 index 000000000..57bd64860 --- /dev/null +++ b/host/lib/convert/convert_item32.cpp @@ -0,0 +1,44 @@ +// +// Copyright 2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> + +#define __DECLARE_ITEM32_CONVERTER(cpu_type, wire_type, xe, htoxx, xxtoh) \ + DECLARE_CONVERTER(cpu_type, 1, wire_type ## _item32_ ## xe, 1, PRIORITY_GENERAL){ \ + const cpu_type ## _t *input = reinterpret_cast<const cpu_type ## _t *>(inputs[0]); \ + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); \ + xx_to_item32_ ## wire_type<htoxx>(input, output, nsamps, scale_factor); \ + } \ + DECLARE_CONVERTER(wire_type ## _item32_ ## xe, 1, cpu_type, 1, PRIORITY_GENERAL){ \ + const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); \ + cpu_type ## _t *output = reinterpret_cast<cpu_type ## _t *>(outputs[0]); \ + item32_ ## wire_type ## _to_xx<xxtoh>(input, output, nsamps, scale_factor); \ + } + +#define _DECLARE_ITEM32_CONVERTER(cpu_type, wire_type) \ + __DECLARE_ITEM32_CONVERTER(cpu_type, wire_type, be, uhd::htonx, uhd::ntohx) \ + __DECLARE_ITEM32_CONVERTER(cpu_type, wire_type, le, uhd::htowx, uhd::wtohx) + +#define DECLARE_ITEM32_CONVERTER(cpu_type) \ + _DECLARE_ITEM32_CONVERTER(cpu_type, sc8) \ + _DECLARE_ITEM32_CONVERTER(cpu_type, sc16) + +DECLARE_ITEM32_CONVERTER(sc16) +DECLARE_ITEM32_CONVERTER(fc32) +DECLARE_ITEM32_CONVERTER(fc64) +_DECLARE_ITEM32_CONVERTER(sc8, sc8) diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp index c7ad62104..e994d97a6 100644 --- a/host/lib/convert/convert_with_neon.cpp +++ b/host/lib/convert/convert_with_neon.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2011 Ettus Research LLC +// Copyright 2011-2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> #include <arm_neon.h> using namespace uhd::convert; @@ -36,8 +37,7 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ vst1_s16((reinterpret_cast<int16_t *>(&output[i])), D9); } - for (; i < nsamps; i++) - output[i] = fc32_to_item32_sc16(input[i], scale_factor); + xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ @@ -56,6 +56,5 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ vst1q_f32((reinterpret_cast<float *>(&output[i])), Q4); } - for (; i < nsamps; i++) - output[i] = item32_sc16_to_fc32(input[i], scale_factor); + item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index 364c4bd1a..b0790755a 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -48,68 +48,6 @@ DECLARE_CONVERTER(sc16_item32_$(end), 1, item32, 1, PRIORITY_GENERAL){ } """ -TMPL_CONV_GEN2_SC16 = """ -DECLARE_CONVERTER($(cpu_type), 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){ - const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - for (size_t i = 0; i < nsamps; i++){ - output[i] = $(to_wire)($(cpu_type)_to_item32_sc16(input[i], scale_factor)); - } -} - -DECLARE_CONVERTER(sc16_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ - const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); - $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]); - - for (size_t i = 0; i < nsamps; i++){ - output[i] = item32_sc16_to_$(cpu_type)($(to_host)(input[i]), scale_factor); - } -} -""" - -TMPL_CONV_GEN2_SC8 = """ -DECLARE_CONVERTER(sc8_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ - const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); - $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]); - $(cpu_type)_t dummy; - size_t num_samps = nsamps; - - if ((size_t(inputs[0]) & 0x3) != 0){ - const item32_t item0 = $(to_host)(*input++); - item32_sc8_to_$(cpu_type)(item0, dummy, *output++, scale_factor); - num_samps--; - } - - const size_t num_pairs = num_samps/2; - for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ - const item32_t item_i = $(to_host)(input[i]); - item32_sc8_to_$(cpu_type)(item_i, output[j], output[j+1], scale_factor); - } - - if (num_samps != num_pairs*2){ - const item32_t item_n = $(to_host)(input[num_pairs]); - item32_sc8_to_$(cpu_type)(item_n, output[num_samps-1], dummy, scale_factor); - } -} - -DECLARE_CONVERTER($(cpu_type), 1, sc8_item32_$(end), 1, PRIORITY_GENERAL){ - const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const size_t num_pairs = nsamps/2; - for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ - const item32_t item = $(cpu_type)_to_item32_sc8(input[j], input[j+1], scale_factor); - output[i] = $(to_wire)(item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = $(cpu_type)_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = $(to_wire)(item); - } -} -""" - TMPL_CONV_USRP1_COMPLEX = """ DECLARE_CONVERTER($(cpu_type), $(width), sc16_item16_usrp1, 1, PRIORITY_GENERAL){ #for $w in range($width) @@ -176,16 +114,6 @@ if __name__ == '__main__': ('be', 'uhd::ntohx', 'uhd::htonx'), ('le', 'uhd::wtohx', 'uhd::htowx'), ): - for cpu_type in 'fc64', 'fc32', 'sc16': - output += parse_tmpl( - TMPL_CONV_GEN2_SC16, - end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type - ) - for cpu_type in 'fc64', 'fc32', 'sc16', 'sc8': - output += parse_tmpl( - TMPL_CONV_GEN2_SC8, - end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type - ) output += parse_tmpl( TMPL_CONV_GEN2_ITEM32, end=end, to_host=to_host, to_wire=to_wire diff --git a/host/lib/convert/sse2_fc32_to_sc16.cpp b/host/lib/convert/sse2_fc32_to_sc16.cpp new file mode 100644 index 000000000..90bf0ed04 --- /dev/null +++ b/host/lib/convert/sse2_fc32_to_sc16.cpp @@ -0,0 +1,103 @@ +// +// Copyright 2011-2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ + const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)); + + #define convert_fc32_1_to_item32_1_nswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ + __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ + \ + /* convert and scale */ \ + __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ + __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ + \ + /* pack + swap 16-bit pairs */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + switch (size_t(input) & 0xf){ + case 0x8: + xx_to_item32_sc16<uhd::htowx>(input, output, 1, scale_factor); i++; + case 0x0: + convert_fc32_1_to_item32_1_nswap_guts(_) + break; + default: convert_fc32_1_to_item32_1_nswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); +} + +DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ + const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)); + + #define convert_fc32_1_to_item32_1_bswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ + __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ + \ + /* convert and scale */ \ + __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ + __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ + \ + /* pack + byteswap -> byteswap 16 bit words */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + switch (size_t(input) & 0xf){ + case 0x8: + xx_to_item32_sc16<uhd::htonx>(input, output, 1, scale_factor); i++; + case 0x0: + convert_fc32_1_to_item32_1_bswap_guts(_) + break; + default: convert_fc32_1_to_item32_1_bswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); +} diff --git a/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp b/host/lib/convert/sse2_fc32_to_sc8.cpp index b633f487c..dd884640d 100644 --- a/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp +++ b/host/lib/convert/sse2_fc32_to_sc8.cpp @@ -21,41 +21,22 @@ using namespace uhd::convert; -UHD_INLINE __m128i pack_sc32_4x_be( +template <const int shuf> +UHD_INLINE __m128i pack_sc32_4x( const __m128 &in0, const __m128 &in1, const __m128 &in2, const __m128 &in3, const __m128 &scalar ){ __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar)); - tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(1, 0, 3, 2)); + tmpi0 = _mm_shuffle_epi32(tmpi0, shuf); __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar)); - tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(1, 0, 3, 2)); + tmpi1 = _mm_shuffle_epi32(tmpi1, shuf); const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1); __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar)); - tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(1, 0, 3, 2)); + tmpi2 = _mm_shuffle_epi32(tmpi2, shuf); __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar)); - tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(1, 0, 3, 2)); - const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3); - - return _mm_packs_epi16(lo, hi); -} - -UHD_INLINE __m128i pack_sc32_4x_le( - const __m128 &in0, const __m128 &in1, - const __m128 &in2, const __m128 &in3, - const __m128 &scalar -){ - __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar)); - tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(2, 3, 0, 1)); - __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar)); - tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(2, 3, 0, 1)); - const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1); - - __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar)); - tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(2, 3, 0, 1)); - __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar)); - tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(2, 3, 0, 1)); + tmpi3 = _mm_shuffle_epi32(tmpi3, shuf); const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3); return _mm_packs_epi16(lo, hi); @@ -66,6 +47,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); const __m128 scalar = _mm_set_ps1(float(scale_factor)); + const int shuf = _MM_SHUFFLE(1, 0, 3, 2); #define convert_fc32_1_to_sc8_item32_1_bswap_guts(_al_) \ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ @@ -76,7 +58,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){ __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \ \ /* convert */ \ - const __m128i tmpi = pack_sc32_4x_be(tmp0, tmp1, tmp2, tmp3, scalar); \ + const __m128i tmpi = pack_sc32_4x<shuf>(tmp0, tmp1, tmp2, tmp3, scalar); \ \ /* store to output */ \ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ @@ -93,16 +75,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = uhd::byteswap(item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = uhd::byteswap(item); - } + xx_to_item32_sc8<uhd::htonx>(input+i, output+(i/2), nsamps-i, scale_factor); } DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ @@ -110,6 +83,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); const __m128 scalar = _mm_set_ps1(float(scale_factor)); + const int shuf = _MM_SHUFFLE(2, 3, 0, 1); #define convert_fc32_1_to_sc8_item32_1_nswap_guts(_al_) \ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ @@ -120,7 +94,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \ \ /* convert */ \ - const __m128i tmpi = pack_sc32_4x_le(tmp0, tmp1, tmp2, tmp3, scalar); \ + const __m128i tmpi = pack_sc32_4x<shuf>(tmp0, tmp1, tmp2, tmp3, scalar); \ \ /* store to output */ \ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ @@ -137,14 +111,5 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = (item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = (item); - } + xx_to_item32_sc8<uhd::htowx>(input+i, output+(i/2), nsamps-i, scale_factor); } diff --git a/host/lib/convert/sse2_fc64_to_sc16.cpp b/host/lib/convert/sse2_fc64_to_sc16.cpp new file mode 100644 index 000000000..f030e9168 --- /dev/null +++ b/host/lib/convert/sse2_fc64_to_sc16.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2011-2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){ + const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor); + + #define convert_fc64_1_to_item32_1_nswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ + __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ + __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ + __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ + \ + /* convert and scale */ \ + __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ + __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ + __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ + __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ + __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ + __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ + \ + /* pack + swap 16-bit pairs */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc64_1_to_item32_1_nswap_guts(_) + } + else{ + convert_fc64_1_to_item32_1_nswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); +} + +DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){ + const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor); + + #define convert_fc64_1_to_item32_1_bswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ + __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ + __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ + __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ + \ + /* convert and scale */ \ + __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ + __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ + __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ + __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ + __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ + __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ + \ + /* pack + byteswap -> byteswap 16 bit words */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc64_1_to_item32_1_bswap_guts(_) + } + else{ + convert_fc64_1_to_item32_1_bswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); +} diff --git a/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp b/host/lib/convert/sse2_fc64_to_sc8.cpp index 405850601..bf3719e13 100644 --- a/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp +++ b/host/lib/convert/sse2_fc64_to_sc8.cpp @@ -30,7 +30,7 @@ UHD_INLINE __m128i pack_sc8_item32_4x( return _mm_packs_epi16(lo, hi); } -UHD_INLINE __m128i pack_sc32_4x_be( +UHD_INLINE __m128i pack_sc32_4x( const __m128d &lo, const __m128d &hi, const __m128d &scalar ){ @@ -39,16 +39,6 @@ UHD_INLINE __m128i pack_sc32_4x_be( return _mm_unpacklo_epi64(tmpi_lo, tmpi_hi); } -UHD_INLINE __m128i pack_sc32_4x_le( - const __m128d &lo, const __m128d &hi, - const __m128d &scalar -){ - const __m128i tmpi_lo = _mm_cvttpd_epi32(_mm_mul_pd(lo, scalar)); - const __m128i tmpi_hi = _mm_cvttpd_epi32(_mm_mul_pd(hi, scalar)); - const __m128i tmpi = _mm_unpacklo_epi64(tmpi_lo, tmpi_hi); - return _mm_shuffle_epi32(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); -} - DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -69,10 +59,10 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){ \ /* interleave */ \ const __m128i tmpi = pack_sc8_item32_4x( \ - pack_sc32_4x_be(tmp0, tmp1, scalar), \ - pack_sc32_4x_be(tmp2, tmp3, scalar), \ - pack_sc32_4x_be(tmp4, tmp5, scalar), \ - pack_sc32_4x_be(tmp6, tmp7, scalar) \ + pack_sc32_4x(tmp0, tmp1, scalar), \ + pack_sc32_4x(tmp2, tmp3, scalar), \ + pack_sc32_4x(tmp4, tmp5, scalar), \ + pack_sc32_4x(tmp6, tmp7, scalar) \ ); \ \ /* store to output */ \ @@ -90,16 +80,7 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = uhd::byteswap(item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = uhd::byteswap(item); - } + xx_to_item32_sc8<uhd::htonx>(input+i, output+(i/2), nsamps-i, scale_factor); } DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){ @@ -121,12 +102,13 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){ __m128d tmp7 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+7)); \ \ /* interleave */ \ - const __m128i tmpi = pack_sc8_item32_4x( \ - pack_sc32_4x_le(tmp0, tmp1, scalar), \ - pack_sc32_4x_le(tmp2, tmp3, scalar), \ - pack_sc32_4x_le(tmp4, tmp5, scalar), \ - pack_sc32_4x_le(tmp6, tmp7, scalar) \ + __m128i tmpi = pack_sc8_item32_4x( \ + pack_sc32_4x(tmp1, tmp0, scalar), \ + pack_sc32_4x(tmp3, tmp2, scalar), \ + pack_sc32_4x(tmp5, tmp4, scalar), \ + pack_sc32_4x(tmp7, tmp6, scalar) \ ); \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); /*byteswap*/\ \ /* store to output */ \ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ @@ -143,14 +125,5 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = (item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = (item); - } + xx_to_item32_sc8<uhd::htowx>(input+i, output+(i/2), nsamps-i, scale_factor); } diff --git a/host/lib/convert/convert_fc32_with_sse2.cpp b/host/lib/convert/sse2_sc16_to_fc32.cpp index 97a3e8cdc..c03e41585 100644 --- a/host/lib/convert/convert_fc32_with_sse2.cpp +++ b/host/lib/convert/sse2_sc16_to_fc32.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -21,91 +21,6 @@ using namespace uhd::convert; -DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ - const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128 scalar = _mm_set_ps1(float(scale_factor)); - - #define convert_fc32_1_to_item32_1_nswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ - __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ - \ - /* convert and scale */ \ - __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ - __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ - \ - /* pack + swap 16-bit pairs */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - switch (size_t(input) & 0xf){ - case 0x8: - output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); i++; - case 0x0: - convert_fc32_1_to_item32_1_nswap_guts(_) - break; - default: convert_fc32_1_to_item32_1_nswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); - } -} - -DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ - const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128 scalar = _mm_set_ps1(float(scale_factor)); - - #define convert_fc32_1_to_item32_1_bswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ - __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ - \ - /* convert and scale */ \ - __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ - __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ - \ - /* pack + byteswap -> byteswap 16 bit words */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - switch (size_t(input) & 0xf){ - case 0x8: - output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); i++; - case 0x0: - convert_fc32_1_to_item32_1_bswap_guts(_) - break; - default: convert_fc32_1_to_item32_1_bswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); - } -} - DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -138,7 +53,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ //dispatch according to alignment switch (size_t(output) & 0xf){ case 0x8: - output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); i++; + item32_sc16_to_xx<uhd::htowx>(input, output, 1, scale_factor); i++; case 0x0: convert_item32_1_to_fc32_1_nswap_guts(_) break; @@ -146,9 +61,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); - } + item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ @@ -182,7 +95,7 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ //dispatch according to alignment switch (size_t(output) & 0xf){ case 0x8: - output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); i++; + item32_sc16_to_xx<uhd::htonx>(input, output, 1, scale_factor); i++; case 0x0: convert_item32_1_to_fc32_1_bswap_guts(_) break; @@ -190,7 +103,5 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); - } + item32_sc16_to_xx<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/convert/convert_fc64_with_sse2.cpp b/host/lib/convert/sse2_sc16_to_fc64.cpp index 6e097e380..66068cad9 100644 --- a/host/lib/convert/convert_fc64_with_sse2.cpp +++ b/host/lib/convert/sse2_sc16_to_fc64.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -21,99 +21,6 @@ using namespace uhd::convert; -DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){ - const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128d scalar = _mm_set1_pd(scale_factor); - - #define convert_fc64_1_to_item32_1_nswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ - __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ - __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ - __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ - \ - /* convert and scale */ \ - __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ - __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ - __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ - __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ - __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ - __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ - \ - /* pack + swap 16-bit pairs */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - if ((size_t(input) & 0xf) == 0){ - convert_fc64_1_to_item32_1_nswap_guts(_) - } - else{ - convert_fc64_1_to_item32_1_nswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = fc64_to_item32_sc16(input[i], scale_factor); - } -} - -DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){ - const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128d scalar = _mm_set1_pd(scale_factor); - - #define convert_fc64_1_to_item32_1_bswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ - __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ - __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ - __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ - \ - /* convert and scale */ \ - __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ - __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ - __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ - __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ - __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ - __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ - \ - /* pack + byteswap -> byteswap 16 bit words */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - if ((size_t(input) & 0xf) == 0){ - convert_fc64_1_to_item32_1_bswap_guts(_) - } - else{ - convert_fc64_1_to_item32_1_bswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = uhd::byteswap(fc64_to_item32_sc16(input[i], scale_factor)); - } -} - DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); @@ -158,9 +65,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc64(input[i], scale_factor); - } + item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){ @@ -206,7 +111,5 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc64(uhd::byteswap(input[i]), scale_factor); - } + item32_sc16_to_xx<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/convert/sse2_sc8_to_fc32.cpp b/host/lib/convert/sse2_sc8_to_fc32.cpp new file mode 100644 index 000000000..c0e561814 --- /dev/null +++ b/host/lib/convert/sse2_sc8_to_fc32.cpp @@ -0,0 +1,132 @@ +// +// Copyright 2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +static const __m128i zeroi = _mm_setzero_si128(); + +template <const int shuf> +UHD_INLINE void unpack_sc32_4x( + const __m128i &in, + __m128 &out0, __m128 &out1, + __m128 &out2, __m128 &out3, + const __m128 &scalar +){ + const __m128i tmplo = _mm_unpacklo_epi8(zeroi, in); /* value in upper 8 bits */ + __m128i tmp0 = _mm_shuffle_epi32(_mm_unpacklo_epi16(zeroi, tmplo), shuf); /* value in upper 16 bits */ + __m128i tmp1 = _mm_shuffle_epi32(_mm_unpackhi_epi16(zeroi, tmplo), shuf); + out0 = _mm_mul_ps(_mm_cvtepi32_ps(tmp0), scalar); + out1 = _mm_mul_ps(_mm_cvtepi32_ps(tmp1), scalar); + + const __m128i tmphi = _mm_unpackhi_epi8(zeroi, in); + __m128i tmp2 = _mm_shuffle_epi32(_mm_unpacklo_epi16(zeroi, tmphi), shuf); + __m128i tmp3 = _mm_shuffle_epi32(_mm_unpackhi_epi16(zeroi, tmphi), shuf); + out2 = _mm_mul_ps(_mm_cvtepi32_ps(tmp2), scalar); + out3 = _mm_mul_ps(_mm_cvtepi32_ps(tmp3), scalar); +} + +DECLARE_CONVERTER(sc8_item32_be, 1, fc32, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 24)); + const int shuf = _MM_SHUFFLE(1, 0, 3, 2); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::ntohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc32_1_bswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack + swap 8-bit pairs */ \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + unpack_sc32_4x<shuf>(tmpi, tmp0, tmp1, tmp2, tmp3, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+2), tmp1); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+4), tmp2); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+6), tmp3); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc32_1_bswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc32_1_bswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::ntohx>(input+i, output+j, num_samps-j, scale_factor); +} + +DECLARE_CONVERTER(sc8_item32_le, 1, fc32, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 24)); + const int shuf = _MM_SHUFFLE(2, 3, 0, 1); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::wtohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc32_1_nswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack + swap 8-bit pairs */ \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + unpack_sc32_4x<shuf>(tmpi, tmp0, tmp1, tmp2, tmp3, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+2), tmp1); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+4), tmp2); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+6), tmp3); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc32_1_nswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc32_1_nswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::wtohx>(input+i, output+j, num_samps-j, scale_factor); +} diff --git a/host/lib/convert/sse2_sc8_to_fc64.cpp b/host/lib/convert/sse2_sc8_to_fc64.cpp new file mode 100644 index 000000000..ef9c0fdb4 --- /dev/null +++ b/host/lib/convert/sse2_sc8_to_fc64.cpp @@ -0,0 +1,151 @@ +// +// Copyright 2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +static const __m128i zeroi = _mm_setzero_si128(); + +UHD_INLINE void unpack_sc32_8x( + const __m128i &in, + __m128d &out0, __m128d &out1, + __m128d &out2, __m128d &out3, + __m128d &out4, __m128d &out5, + __m128d &out6, __m128d &out7, + const __m128d &scalar +){ + const int shuf = _MM_SHUFFLE(1, 0, 3, 2); + __m128i tmp; + + const __m128i tmplo = _mm_unpacklo_epi8(zeroi, in); /* value in upper 8 bits */ + tmp = _mm_unpacklo_epi16(zeroi, tmplo); /* value in upper 16 bits */ + out0 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out1 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_unpackhi_epi16(zeroi, tmplo); + out2 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out3 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + + const __m128i tmphi = _mm_unpackhi_epi8(zeroi, in); + tmp = _mm_unpacklo_epi16(zeroi, tmphi); + out4 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out5 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_unpackhi_epi16(zeroi, tmphi); + out6 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out7 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); +} + +DECLARE_CONVERTER(sc8_item32_be, 1, fc64, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor/(1 << 24)); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::ntohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc64_1_bswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack */ \ + __m128d tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; \ + unpack_sc32_8x(tmpi, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+1), tmp1); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+2), tmp2); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+3), tmp3); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+4), tmp4); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+5), tmp5); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+6), tmp6); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+7), tmp7); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc64_1_bswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc64_1_bswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::ntohx>(input+i, output+j, num_samps-j, scale_factor); +} + +DECLARE_CONVERTER(sc8_item32_le, 1, fc64, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor/(1 << 24)); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::wtohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc64_1_nswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack */ \ + __m128d tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); /*byteswap*/\ + unpack_sc32_8x(tmpi, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+1), tmp1); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+2), tmp2); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+3), tmp3); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+4), tmp4); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+5), tmp5); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+6), tmp6); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+7), tmp7); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc64_1_nswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc64_1_nswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::wtohx>(input+i, output+j, num_samps-j, scale_factor); +} diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 8e8ea5ea8..6524a8412 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -82,7 +82,11 @@ SET_SOURCE_FILES_PROPERTIES( ######################################################################## # Setup UDP ######################################################################## -LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp) +IF(WIN32) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/udp_wsa_zero_copy.cpp) +ELSE() + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp) +ENDIF() #On windows, the boost asio implementation uses the winsock2 library. #Note: we exclude the .lib extension for cygwin and mingw platforms. @@ -97,6 +101,7 @@ CHECK_INCLUDE_FILE_CXX(atlbase.h HAVE_ATLBASE_H) IF(HAVE_ATLBASE_H) SET_SOURCE_FILES_PROPERTIES( ${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/udp_wsa_zero_copy.cpp PROPERTIES COMPILE_DEFINITIONS "HAVE_ATLBASE_H" ) ENDIF(HAVE_ATLBASE_H) diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 3e67264cd..28bff9709 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -21,6 +21,7 @@ #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <boost/foreach.hpp> +#include <boost/make_shared.hpp> #include <boost/thread/thread.hpp> #include <list> @@ -61,8 +62,18 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ * \return true for completion, false for timeout */ UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, bool &completed){ - const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); + //already completed by a previous call? + if (completed) return true; + + //perform a non-blocking event handle + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + libusb_handle_events_timeout(ctx, &tv); + if (completed) return true; + //finish the rest with a timeout loop + const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); while (not completed and (boost::get_system_time() < timeout_time)){ timeval tv; tv.tv_sec = 0; @@ -82,21 +93,18 @@ class libusb_zero_copy_mrb : public managed_recv_buffer{ public: libusb_zero_copy_mrb(libusb_transfer *lut, const size_t frame_size): _ctx(libusb::session::get_global_session()->get_context()), - _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ } + _lut(lut), _frame_size(frame_size) { /* NOP */ } void release(void){ - if (_expired) return; completed = false; _lut->length = _frame_size; //always reset length UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); - _expired = true; } sptr get_new(const double timeout, size_t &index){ if (wait_for_completion(_ctx, timeout, completed)){ index++; - _expired = false; - return make_managed_buffer(this); + return make(this, _lut->buffer, _lut->actual_length); } return managed_recv_buffer::sptr(); } @@ -104,12 +112,8 @@ public: bool completed; private: - const void *get_buff(void) const{return _lut->buffer;} - size_t get_size(void) const{return _lut->actual_length;} - libusb_context *_ctx; libusb_transfer *_lut; - bool _expired; const size_t _frame_size; }; @@ -122,22 +126,18 @@ class libusb_zero_copy_msb : public managed_send_buffer{ public: libusb_zero_copy_msb(libusb_transfer *lut, const size_t frame_size): _ctx(libusb::session::get_global_session()->get_context()), - _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ } + _lut(lut), _frame_size(frame_size) { completed = true; } - void commit(size_t len){ - if (_expired) return; + void release(void){ completed = false; - _lut->length = len; - if (len == 0) libusb_async_cb(_lut); - else UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); - _expired = true; + _lut->length = size(); + UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); } sptr get_new(const double timeout, size_t &index){ if (wait_for_completion(_ctx, timeout, completed)){ index++; - _expired = false; - return make_managed_buffer(this); + return make(this, _lut->buffer, _frame_size); } return managed_send_buffer::sptr(); } @@ -145,12 +145,8 @@ public: bool completed; private: - void *get_buff(void) const{return _lut->buffer;} - size_t get_size(void) const{return _frame_size;} - libusb_context *_ctx; libusb_transfer *_lut; - bool _expired; const size_t _frame_size; }; @@ -181,13 +177,30 @@ public: _handle->claim_interface(recv_interface); _handle->claim_interface(send_interface); + //flush the buffers out of the recv endpoint + //limit the flushing to at most one second + for (size_t i = 0; i < 100; i++) + { + unsigned char buff[512]; + int transfered = 0; + const int status = libusb_bulk_transfer( + _handle->get(), // dev_handle + (recv_endpoint & 0x7f) | 0x80, // endpoint + static_cast<unsigned char *>(buff), + sizeof(buff), + &transfered, //bytes xfered + 10 //timeout ms + ); + if (status == LIBUSB_ERROR_TIMEOUT) break; + } + //allocate libusb transfer structs and managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ libusb_transfer *lut = libusb_alloc_transfer(0); UHD_ASSERT_THROW(lut != NULL); - _mrb_pool.push_back(boost::shared_ptr<libusb_zero_copy_mrb>(new libusb_zero_copy_mrb(lut, this->get_recv_frame_size()))); + _mrb_pool.push_back(boost::make_shared<libusb_zero_copy_mrb>(lut, this->get_recv_frame_size())); libusb_fill_bulk_transfer( lut, // transfer @@ -210,7 +223,7 @@ public: libusb_transfer *lut = libusb_alloc_transfer(0); UHD_ASSERT_THROW(lut != NULL); - _msb_pool.push_back(boost::shared_ptr<libusb_zero_copy_msb>(new libusb_zero_copy_msb(lut, this->get_send_frame_size()))); + _msb_pool.push_back(boost::make_shared<libusb_zero_copy_msb>(lut, this->get_send_frame_size())); libusb_fill_bulk_transfer( lut, // transfer @@ -224,7 +237,6 @@ public: ); _all_luts.push_back(lut); - _msb_pool.back()->commit(0); } } diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 205c7a3a3..4b96199e2 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -23,6 +23,8 @@ #include <uhd/convert.hpp> #include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> @@ -31,6 +33,9 @@ #include <boost/foreach.hpp> #include <boost/function.hpp> #include <boost/format.hpp> +#include <boost/bind.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/barrier.hpp> #include <iostream> #include <vector> @@ -73,12 +78,23 @@ public: set_alignment_failure_threshold(1000); } + ~recv_packet_handler(void){ + _task_handlers.clear(); + } + //! Resize the number of transport channels void resize(const size_t size){ if (this->size() == size) return; + _task_handlers.clear(); _props.resize(size); //re-initialize all buffers infos by re-creating the vector _buffers_infos = std::vector<buffers_info_type>(4, buffers_info_type(size)); + _task_barrier_entry.resize(size); + _task_barrier_exit.resize(size); + _task_handlers.resize(size); + for (size_t i = 1/*skip 0*/; i < size; i++){ + _task_handlers[i] = task::make(boost::bind(&recv_packet_handler::converter_thread_task, this, i)); + }; } //! Get the channel width of this handler @@ -125,7 +141,7 @@ public: //! Set the conversion routine for all channels void set_converter(const uhd::convert::id_type &id){ - _io_buffs.resize(id.num_outputs); + _num_outputs = id.num_outputs; _converter = uhd::convert::get_converter(id)(); this->set_scale_factor(1/32767.); //update after setting converter _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.input_format); @@ -207,7 +223,7 @@ private: handle_overflow_type handle_overflow; }; std::vector<xport_chan_props_type> _props; - std::vector<void *> _io_buffs; //used in conversion + size_t _num_outputs; size_t _bytes_per_otw_item; //used in conversion size_t _bytes_per_cpu_item; //used in conversion uhd::convert::converter::sptr _converter; //used in conversion @@ -512,24 +528,19 @@ private: //extract the number of samples available to copy const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_otw_item; - const size_t nsamps_to_copy = std::min(nsamps_per_buff*_io_buffs.size(), nsamps_available); + const size_t nsamps_to_copy = std::min(nsamps_per_buff*_num_outputs, nsamps_available); const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_otw_item; - const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_io_buffs.size(); + const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_num_outputs; - size_t buff_index = 0; - BOOST_FOREACH(per_buffer_info_type &buff_info, info){ + //setup the data to share with converter threads + _convert_nsamps = nsamps_to_copy_per_io_buff; + _convert_buffs = &buffs; + _convert_buffer_offset_bytes = buffer_offset_bytes; + _convert_bytes_to_copy = bytes_to_copy; - //fill a vector with pointers to the io buffers - BOOST_FOREACH(void *&io_buff, _io_buffs){ - io_buff = reinterpret_cast<char *>(buffs[buff_index++]) + buffer_offset_bytes; - } + //perform N channels of conversion + converter_thread_task(0); - //copy-convert the samples from the recv buffer - _converter->conv(buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff); - - //update the rx copy buffer to reflect the bytes copied - buff_info.copy_buff += bytes_to_copy; - } //update the copy buffer's availability info.data_bytes_to_copy -= bytes_to_copy; @@ -538,15 +549,53 @@ private: metadata.fragment_offset = info.fragment_offset_in_samps; info.fragment_offset_in_samps += nsamps_to_copy; //set for next call - //done with buffers? this action releases buffers in-order - if (not metadata.more_fragments){ - BOOST_FOREACH(per_buffer_info_type &buff_info, info){ - buff_info.buff.reset(); //effectively a release - } + return nsamps_to_copy_per_io_buff; + } + + /******************************************************************* + * Perform one thread's work of the conversion task. + * The entry and exit use a dual synchronization barrier, + * to wait for data to become ready and block until completion. + ******************************************************************/ + UHD_INLINE void converter_thread_task(const size_t index) + { + _task_barrier_entry.wait(); + + //shortcut references to local data structures + buffers_info_type &buff_info = get_curr_buffer_info(); + per_buffer_info_type &info = buff_info[index]; + const rx_streamer::buffs_type &buffs = *_convert_buffs; + + //fill IO buffs with pointers into the output buffer + void *io_buffs[4/*max interleave*/]; + for (size_t i = 0; i < _num_outputs; i++){ + char *b = reinterpret_cast<char *>(buffs[index*_num_outputs + i]); + io_buffs[i] = b + _convert_buffer_offset_bytes; } + const ref_vector<void *> out_buffs(io_buffs, _num_outputs); - return nsamps_to_copy_per_io_buff; + //perform the conversion operation + _converter->conv(info.copy_buff, out_buffs, _convert_nsamps); + + //advance the pointer for the source buffer + info.copy_buff += _convert_bytes_to_copy; + + //release the buffer if fully consumed + if (buff_info.data_bytes_to_copy == _convert_bytes_to_copy){ + info.buff.reset(); //effectively a release + } + + _task_barrier_exit.wait(); } + + //! Shared variables for the worker threads + reusable_barrier _task_barrier_entry, _task_barrier_exit; + std::vector<task::sptr> _task_handlers; + size_t _convert_nsamps; + const rx_streamer::buffs_type *_convert_buffs; + size_t _convert_buffer_offset_bytes; + size_t _convert_bytes_to_copy; + }; class recv_packet_streamer : public recv_packet_handler, public rx_streamer{ diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index 46c98afea..8f943effb 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -23,6 +23,8 @@ #include <uhd/convert.hpp> #include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> @@ -58,12 +60,23 @@ public: this->resize(size); } + ~send_packet_handler(void){ + _task_handlers.clear(); + } + //! Resize the number of transport channels void resize(const size_t size){ if (this->size() == size) return; + _task_handlers.clear(); _props.resize(size); static const boost::uint64_t zero = 0; _zero_buffs.resize(size, &zero); + _task_barrier_entry.resize(size); + _task_barrier_exit.resize(size); + _task_handlers.resize(size); + for (size_t i = 1/*skip 0*/; i < size; i++){ + _task_handlers[i] = task::make(boost::bind(&send_packet_handler::converter_thread_task, this, i)); + }; } //! Get the channel width of this handler @@ -77,6 +90,12 @@ public: _header_offset_words32 = header_offset_words32; } + //! Set the stream ID for a specific channel (or no SID) + void set_xport_chan_sid(const size_t xport_chan, const bool has_sid, const boost::uint32_t sid = 0){ + _props.at(xport_chan).has_sid = has_sid; + _props.at(xport_chan).sid = sid; + } + //! Set the rate of ticks per second void set_tick_rate(const double rate){ _tick_rate = rate; @@ -98,7 +117,7 @@ public: //! Set the conversion routine for all channels void set_converter(const uhd::convert::id_type &id){ - _io_buffs.resize(id.num_inputs); + _num_inputs = id.num_inputs; _converter = uhd::convert::get_converter(id)(); this->set_scale_factor(32767.); //update after setting converter _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.output_format); @@ -133,7 +152,7 @@ public: //translate the metadata to vrt if packet info vrt::if_packet_info_t if_packet_info; if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; - if_packet_info.has_sid = false; + //if_packet_info.has_sid = false; //set per channel if_packet_info.has_cid = false; if_packet_info.has_tlr = true; if_packet_info.has_tsi = false; @@ -195,10 +214,14 @@ private: size_t _header_offset_words32; double _tick_rate, _samp_rate; struct xport_chan_props_type{ + xport_chan_props_type(void):has_sid(false){} get_buff_type get_buff; + bool has_sid; + boost::uint32_t sid; + managed_send_buffer::sptr buff; }; std::vector<xport_chan_props_type> _props; - std::vector<const void *> _io_buffs; //used in conversion + size_t _num_inputs; size_t _bytes_per_otw_item; //used in conversion size_t _bytes_per_cpu_item; //used in conversion uhd::convert::converter::sptr _converter; //used in conversion @@ -217,36 +240,77 @@ private: const size_t buffer_offset_bytes = 0 ){ //load the rest of the if_packet_info in here - if_packet_info.num_payload_bytes = nsamps_per_buff*_io_buffs.size()*_bytes_per_otw_item; + if_packet_info.num_payload_bytes = nsamps_per_buff*_num_inputs*_bytes_per_otw_item; if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t); if_packet_info.packet_count = _next_packet_seq; - size_t buff_index = 0; + //get a buffer for each channel or timeout BOOST_FOREACH(xport_chan_props_type &props, _props){ - managed_send_buffer::sptr buff = props.get_buff(timeout); - if (buff.get() == NULL) return 0; //timeout - - //fill a vector with pointers to the io buffers - BOOST_FOREACH(const void *&io_buff, _io_buffs){ - io_buff = reinterpret_cast<const char *>(buffs[buff_index++]) + buffer_offset_bytes; - } - boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32; - - //pack metadata into a vrt header - _vrt_packer(otw_mem, if_packet_info); - otw_mem += if_packet_info.num_header_words32; + if (not props.buff) props.buff = props.get_buff(timeout); + if (not props.buff) return 0; //timeout + } - //copy-convert the samples into the send buffer - _converter->conv(_io_buffs, otw_mem, nsamps_per_buff); + //setup the data to share with converter threads + _convert_nsamps = nsamps_per_buff; + _convert_buffs = &buffs; + _convert_buffer_offset_bytes = buffer_offset_bytes; + _convert_if_packet_info = &if_packet_info; - //commit the samples to the zero-copy interface - size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); - buff->commit(num_bytes_total); + //perform N channels of conversion + converter_thread_task(0); - } _next_packet_seq++; //increment sequence after commits return nsamps_per_buff; } + + /******************************************************************* + * Perform one thread's work of the conversion task. + * The entry and exit use a dual synchronization barrier, + * to wait for data to become ready and block until completion. + ******************************************************************/ + UHD_INLINE void converter_thread_task(const size_t index) + { + _task_barrier_entry.wait(); + + //shortcut references to local data structures + managed_send_buffer::sptr &buff = _props[index].buff; + vrt::if_packet_info_t if_packet_info = *_convert_if_packet_info; + const tx_streamer::buffs_type &buffs = *_convert_buffs; + + //fill IO buffs with pointers into the output buffer + const void *io_buffs[4/*max interleave*/]; + for (size_t i = 0; i < _num_inputs; i++){ + const char *b = reinterpret_cast<const char *>(buffs[index*_num_inputs + i]); + io_buffs[i] = b + _convert_buffer_offset_bytes; + } + const ref_vector<const void *> in_buffs(io_buffs, _num_inputs); + + //pack metadata into a vrt header + boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32; + if_packet_info.has_sid = _props[index].has_sid; + if_packet_info.sid = _props[index].sid; + _vrt_packer(otw_mem, if_packet_info); + otw_mem += if_packet_info.num_header_words32; + + //perform the conversion operation + _converter->conv(in_buffs, otw_mem, _convert_nsamps); + + //commit the samples to the zero-copy interface + const size_t num_vita_words32 = _header_offset_words32+if_packet_info.num_packet_words32; + buff->commit(num_vita_words32*sizeof(boost::uint32_t)); + buff.reset(); //effectively a release + + _task_barrier_exit.wait(); + } + + //! Shared variables for the worker threads + reusable_barrier _task_barrier_entry, _task_barrier_exit; + std::vector<task::sptr> _task_handlers; + size_t _convert_nsamps; + const tx_streamer::buffs_type *_convert_buffs; + size_t _convert_buffer_offset_bytes; + vrt::if_packet_info_t *_convert_if_packet_info; + }; class send_packet_streamer : public send_packet_handler, public tx_streamer{ diff --git a/host/lib/transport/udp_wsa_zero_copy.cpp b/host/lib/transport/udp_wsa_zero_copy.cpp new file mode 100644 index 000000000..6fe4e3cad --- /dev/null +++ b/host/lib/transport/udp_wsa_zero_copy.cpp @@ -0,0 +1,300 @@ +// +// 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 +// 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 "udp_common.hpp" +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/transport/udp_simple.hpp> //mtu +#include <uhd/transport/buffer_pool.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <boost/format.hpp> +#include <vector> + +using namespace uhd; +using namespace uhd::transport; +namespace asio = boost::asio; + +//A reasonable number of frames for send/recv and async/sync +static const size_t DEFAULT_NUM_FRAMES = 32; + +/*********************************************************************** + * Check registry for correct fast-path setting (windows only) + **********************************************************************/ +#ifdef HAVE_ATLBASE_H +#define CHECK_REG_SEND_THRESH +#include <atlbase.h> //CRegKey +static void check_registry_for_fast_send_threshold(const size_t mtu){ + static bool warned = false; + if (warned) return; //only allow one printed warning per process + + CRegKey reg_key; + DWORD threshold = 1024; //system default when threshold is not specified + if ( + reg_key.Open(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\AFD\\Parameters", KEY_READ) != ERROR_SUCCESS or + reg_key.QueryDWORDValue("FastSendDatagramThreshold", threshold) != ERROR_SUCCESS or threshold < mtu + ){ + UHD_MSG(warning) << boost::format( + "The MTU (%d) is larger than the FastSendDatagramThreshold (%d)!\n" + "This will negatively affect the transmit performance.\n" + "See the transport application notes for more detail.\n" + ) % mtu % threshold << std::endl; + warned = true; + } + reg_key.Close(); +} +#endif /*HAVE_ATLBASE_H*/ + +/*********************************************************************** + * Static initialization to take care of WSA init and cleanup + **********************************************************************/ +struct uhd_wsa_control{ + uhd_wsa_control(void){ + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); /*windows socket startup */ + } + + ~uhd_wsa_control(void){ + WSACleanup(); + } +}; + +/*********************************************************************** + * Reusable managed receiver buffer: + * - Initialize with memory and a release callback. + * - Call get new with a length in bytes to re-use. + **********************************************************************/ +class udp_zero_copy_asio_mrb : public managed_recv_buffer{ +public: + udp_zero_copy_asio_mrb(void *mem, int sock_fd, const size_t frame_size): + _sock_fd(sock_fd), _frame_size(frame_size) + { + _wsa_buff.buf = reinterpret_cast<char *>(mem); + ZeroMemory(&_overlapped, sizeof(_overlapped)); + _overlapped.hEvent = WSACreateEvent(); + UHD_ASSERT_THROW(_overlapped.hEvent != WSA_INVALID_EVENT); + this->release(); //makes buffer available via get_new + } + + ~udp_zero_copy_asio_mrb(void){ + WSACloseEvent(_overlapped.hEvent); + } + + void release(void){ + _wsa_buff.len = _frame_size; + _flags = 0; + WSARecv(_sock_fd, &_wsa_buff, 1, &_wsa_buff.len, &_flags, &_overlapped, NULL); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + const DWORD result = WSAWaitForMultipleEvents( + 1, &_overlapped.hEvent, true, DWORD(timeout*1000), true + ); + if (result == WSA_WAIT_TIMEOUT) return managed_recv_buffer::sptr(); + index++; //advances the caller's buffer + + WSAGetOverlappedResult(_sock_fd, &_overlapped, &_wsa_buff.len, true, &_flags); + + WSAResetEvent(_overlapped.hEvent); + return make(this, _wsa_buff.buf, _wsa_buff.len); + } + +private: + int _sock_fd; + const size_t _frame_size; + WSAOVERLAPPED _overlapped; + WSABUF _wsa_buff; + DWORD _flags; +}; + +/*********************************************************************** + * Reusable managed send buffer: + * - committing the buffer calls the asynchronous socket send + * - getting a new buffer performs the blocking wait for completion + **********************************************************************/ +class udp_zero_copy_asio_msb : public managed_send_buffer{ +public: + udp_zero_copy_asio_msb(void *mem, int sock_fd, const size_t frame_size): + _sock_fd(sock_fd), _frame_size(frame_size) + { + _wsa_buff.buf = reinterpret_cast<char *>(mem); + ZeroMemory(&_overlapped, sizeof(_overlapped)); + _overlapped.hEvent = WSACreateEvent(); + UHD_ASSERT_THROW(_overlapped.hEvent != WSA_INVALID_EVENT); + WSASetEvent(_overlapped.hEvent); //makes buffer available via get_new + } + + ~udp_zero_copy_asio_msb(void){ + WSACloseEvent(_overlapped.hEvent); + } + + void release(void){ + _wsa_buff.len = size(); + WSASend(_sock_fd, &_wsa_buff, 1, NULL, 0, &_overlapped, NULL); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + const DWORD result = WSAWaitForMultipleEvents( + 1, &_overlapped.hEvent, true, DWORD(timeout*1000), true + ); + if (result == WSA_WAIT_TIMEOUT) return managed_send_buffer::sptr(); + index++; //advances the caller's buffer + + WSAResetEvent(_overlapped.hEvent); + _wsa_buff.len = _frame_size; + return make(this, _wsa_buff.buf, _wsa_buff.len); + } + +private: + int _sock_fd; + const size_t _frame_size; + WSAOVERLAPPED _overlapped; + WSABUF _wsa_buff; +}; + +/*********************************************************************** + * Zero Copy UDP implementation with WSA: + * + * This is not a true zero copy implementation as each + * send and recv requires a copy operation to/from userspace. + * + * For receive, use a blocking recv() call on the socket. + * This has better performance than the overlapped IO. + * For send, use overlapped IO to submit async sends. + **********************************************************************/ +class udp_zero_copy_wsa_impl : public udp_zero_copy{ +public: + typedef boost::shared_ptr<udp_zero_copy_wsa_impl> sptr; + + udp_zero_copy_wsa_impl( + const std::string &addr, + const std::string &port, + const device_addr_t &hints + ): + _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))), + _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))), + _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))), + _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), + _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), + _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), + _next_recv_buff_index(0), _next_send_buff_index(0) + { + #ifdef CHECK_REG_SEND_THRESH + check_registry_for_fast_send_threshold(this->get_send_frame_size()); + #endif /*CHECK_REG_SEND_THRESH*/ + + UHD_MSG(status) << boost::format("Creating WSA UDP transport for %s:%s") % addr % port << std::endl; + static uhd_wsa_control uhd_wsa; //makes wsa start happen via lazy initialization + + UHD_ASSERT_THROW(_num_send_frames <= WSA_MAXIMUM_WAIT_EVENTS); + + //resolve the address + asio::io_service io_service; + asio::ip::udp::resolver resolver(io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port); + asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + //create the socket + _sock_fd = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED); + if (_sock_fd == INVALID_SOCKET){ + const DWORD error = WSAGetLastError(); + throw uhd::os_error(str(boost::format("WSASocket() failed with error %d") % error)); + } + + //set the socket non-blocking for recv + //u_long mode = 1; + //ioctlsocket(_sock_fd, FIONBIO, &mode); + + //resize the socket buffers + const int recv_buff_size = int(hints.cast<double>("recv_buff_size", 0.0)); + const int send_buff_size = int(hints.cast<double>("send_buff_size", 0.0)); + if (recv_buff_size > 0) setsockopt(_sock_fd, SOL_SOCKET, SO_RCVBUF, (const char *)&recv_buff_size, sizeof(recv_buff_size)); + if (send_buff_size > 0) setsockopt(_sock_fd, SOL_SOCKET, SO_SNDBUF, (const char *)&send_buff_size, sizeof(send_buff_size)); + + //connect the socket so we can send/recv + const asio::ip::udp::endpoint::data_type &servaddr = *receiver_endpoint.data(); + if (WSAConnect(_sock_fd, (const struct sockaddr *)&servaddr, sizeof(servaddr), NULL, NULL, NULL, NULL) != 0){ + const DWORD error = WSAGetLastError(); + closesocket(_sock_fd); + throw uhd::os_error(str(boost::format("WSAConnect() failed with error %d") % error)); + } + + //allocate re-usable managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::shared_ptr<udp_zero_copy_asio_mrb>( + new udp_zero_copy_asio_mrb(_recv_buffer_pool->at(i), _sock_fd, get_recv_frame_size()) + )); + } + + //allocate re-usable managed send buffers + for (size_t i = 0; i < get_num_send_frames(); i++){ + _msb_pool.push_back(boost::shared_ptr<udp_zero_copy_asio_msb>( + new udp_zero_copy_asio_msb(_send_buffer_pool->at(i), _sock_fd, get_send_frame_size()) + )); + } + } + + ~udp_zero_copy_wsa_impl(void){ + closesocket(_sock_fd); + } + + /******************************************************************* + * Receive implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_recv_buffer::sptr get_recv_buff(double timeout){ + if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index); + } + + size_t get_num_recv_frames(void) const {return _num_recv_frames;} + size_t get_recv_frame_size(void) const {return _recv_frame_size;} + + /******************************************************************* + * Send implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_send_buffer::sptr get_send_buff(double timeout){ + if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0; + return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index); + } + + size_t get_num_send_frames(void) const {return _num_send_frames;} + size_t get_send_frame_size(void) const {return _send_frame_size;} + +private: + //memory management -> buffers and fifos + const size_t _recv_frame_size, _num_recv_frames; + const size_t _send_frame_size, _num_send_frames; + buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_msb> > _msb_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_mrb> > _mrb_pool; + size_t _next_recv_buff_index, _next_send_buff_index; + + //socket guts + SOCKET _sock_fd; +}; + +/*********************************************************************** + * UDP zero copy make function + **********************************************************************/ +udp_zero_copy::sptr udp_zero_copy::make( + const std::string &addr, + const std::string &port, + const device_addr_t &hints +){ + return sptr(new udp_zero_copy_wsa_impl(addr, port, hints)); +} diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 0ccc92b82..9125be53a 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -18,12 +18,13 @@ #include "udp_common.hpp" #include <uhd/transport/udp_zero_copy.hpp> #include <uhd/transport/udp_simple.hpp> //mtu -#include <uhd/transport/bounded_buffer.hpp> #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> +#include <uhd/utils/atomic.hpp> #include <boost/format.hpp> -#include <list> +#include <boost/make_shared.hpp> +#include <vector> using namespace uhd; using namespace uhd::transport; @@ -61,66 +62,71 @@ static void check_registry_for_fast_send_threshold(const size_t mtu){ /*********************************************************************** * Reusable managed receiver buffer: - * - Initialize with memory and a release callback. - * - Call get new with a length in bytes to re-use. + * - get_new performs the recv operation **********************************************************************/ class udp_zero_copy_asio_mrb : public managed_recv_buffer{ public: - udp_zero_copy_asio_mrb(void *mem, bounded_buffer<udp_zero_copy_asio_mrb *> &pending): - _mem(mem), _len(0), _pending(pending){/* NOP */} + udp_zero_copy_asio_mrb(void *mem, int sock_fd, const size_t frame_size): + _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ } void release(void){ - if (_len == 0) return; - _pending.push_with_haste(this); - _len = 0; + _claimer.release(); } - sptr get_new(size_t len){ - _len = len; - return make_managed_buffer(this); - } + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + if (not _claimer.claim_with_wait(timeout)) return sptr(); - template <class T> T cast(void) const{return static_cast<T>(_mem);} + #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported + _len = ::recv(_sock_fd, (char *)_mem, _frame_size, MSG_DONTWAIT); + if (_len > 0){ + index++; //advances the caller's buffer + return make(this, _mem, size_t(_len)); + } + #endif -private: - const void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} + if (wait_for_recv_ready(_sock_fd, timeout)){ + _len = ::recv(_sock_fd, (char *)_mem, _frame_size, 0); + index++; //advances the caller's buffer + return make(this, _mem, size_t(_len)); + } + _claimer.release(); //undo claim + return sptr(); //null for timeout + } + +private: void *_mem; - size_t _len; - bounded_buffer<udp_zero_copy_asio_mrb *> &_pending; + int _sock_fd; + size_t _frame_size; + ssize_t _len; + simple_claimer _claimer; }; /*********************************************************************** * Reusable managed send buffer: - * - Initialize with memory and a commit callback. - * - Call get new with a length in bytes to re-use. + * - commit performs the send operation **********************************************************************/ class udp_zero_copy_asio_msb : public managed_send_buffer{ public: - udp_zero_copy_asio_msb(void *mem, bounded_buffer<udp_zero_copy_asio_msb *> &pending, int sock_fd): - _mem(mem), _len(0), _pending(pending), _sock_fd(sock_fd){/* NOP */} - - void commit(size_t len){ - if (_len == 0) return; - ::send(_sock_fd, this->cast<const char *>(), len, 0); - _pending.push_with_haste(this); - _len = 0; + udp_zero_copy_asio_msb(void *mem, int sock_fd, const size_t frame_size): + _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ } + + void release(void){ + UHD_ASSERT_THROW(::send(_sock_fd, (const char *)_mem, size(), 0) == ssize_t(size())); + _claimer.release(); } - sptr get_new(size_t len){ - _len = len; - return make_managed_buffer(this); + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + if (not _claimer.claim_with_wait(timeout)) return sptr(); + index++; //advances the caller's buffer + return make(this, _mem, _frame_size); } private: - void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} - void *_mem; - size_t _len; - bounded_buffer<udp_zero_copy_asio_msb *> &_pending; int _sock_fd; + size_t _frame_size; + simple_claimer _claimer; }; /*********************************************************************** @@ -145,8 +151,7 @@ public: _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), - _pending_recv_buffs(_num_recv_frames), - _pending_send_buffs(_num_send_frames) + _next_recv_buff_index(0), _next_send_buff_index(0) { UHD_LOG << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; @@ -167,18 +172,16 @@ public: //allocate re-usable managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _mrb_pool.push_back(udp_zero_copy_asio_mrb( - _recv_buffer_pool->at(i), _pending_recv_buffs + _mrb_pool.push_back(boost::make_shared<udp_zero_copy_asio_mrb>( + _recv_buffer_pool->at(i), _sock_fd, get_recv_frame_size() )); - _pending_recv_buffs.push_with_haste(&_mrb_pool.back()); } //allocate re-usable managed send buffers for (size_t i = 0; i < get_num_send_frames(); i++){ - _msb_pool.push_back(udp_zero_copy_asio_msb( - _send_buffer_pool->at(i), _pending_send_buffs, _sock_fd + _msb_pool.push_back(boost::make_shared<udp_zero_copy_asio_msb>( + _send_buffer_pool->at(i), _sock_fd, get_send_frame_size() )); - _pending_send_buffs.push_with_haste(&_msb_pool.back()); } } @@ -198,29 +201,11 @@ public: /******************************************************************* * Receive implementation: - * - * Perform a non-blocking receive for performance, - * and then fall back to a blocking receive with timeout. - * Return the managed receive buffer with the new length. - * When the caller is finished with the managed buffer, - * the managed receive buffer is released back into the queue. + * Block on the managed buffer's get call and advance the index. ******************************************************************/ managed_recv_buffer::sptr get_recv_buff(double timeout){ - udp_zero_copy_asio_mrb *mrb = NULL; - if (_pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){ - - #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported - ssize_t ret = ::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, MSG_DONTWAIT); - if (ret > 0) return mrb->get_new(ret); - #endif - - if (wait_for_recv_ready(_sock_fd, timeout)) return mrb->get_new( - ::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, 0) - ); - - _pending_recv_buffs.push_with_haste(mrb); //timeout: return the managed buffer to the queue - } - return managed_recv_buffer::sptr(); + if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index); } size_t get_num_recv_frames(void) const {return _num_recv_frames;} @@ -228,18 +213,11 @@ public: /******************************************************************* * Send implementation: - * - * Get a managed receive buffer immediately with max length set. - * The caller will fill the buffer and commit it when finished. - * The commit routine will perform a blocking send operation, - * and push the managed send buffer back into the queue. + * Block on the managed buffer's get call and advance the index. ******************************************************************/ managed_send_buffer::sptr get_send_buff(double timeout){ - udp_zero_copy_asio_msb *msb = NULL; - if (_pending_send_buffs.pop_with_timed_wait(msb, timeout)){ - return msb->get_new(_send_frame_size); - } - return managed_send_buffer::sptr(); + if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0; + return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index); } size_t get_num_send_frames(void) const {return _num_send_frames;} @@ -250,10 +228,9 @@ private: const size_t _recv_frame_size, _num_recv_frames; const size_t _send_frame_size, _num_send_frames; buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; - bounded_buffer<udp_zero_copy_asio_mrb *> _pending_recv_buffs; - bounded_buffer<udp_zero_copy_asio_msb *> _pending_send_buffs; - std::list<udp_zero_copy_asio_msb> _msb_pool; - std::list<udp_zero_copy_asio_mrb> _mrb_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_msb> > _msb_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_mrb> > _mrb_pool; + size_t _next_recv_buff_index, _next_send_buff_index; //asio guts -> socket and service asio::io_service _io_service; diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/transport/usb_zero_copy_wrapper.cpp index 3571ed856..d04244ca9 100644 --- a/host/lib/transport/usb_zero_copy_wrapper.cpp +++ b/host/lib/transport/usb_zero_copy_wrapper.cpp @@ -16,45 +16,64 @@ // #include <uhd/transport/usb_zero_copy.hpp> -#include <uhd/transport/bounded_buffer.hpp> #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <boost/foreach.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/bind.hpp> #include <vector> #include <iostream> +using namespace uhd; using namespace uhd::transport; +static const boost::posix_time::time_duration AUTOFLUSH_TIMEOUT(boost::posix_time::milliseconds(1)); + /*********************************************************************** * USB zero copy wrapper - managed receive buffer **********************************************************************/ class usb_zero_copy_wrapper_mrb : public managed_recv_buffer{ public: - usb_zero_copy_wrapper_mrb(bounded_buffer<usb_zero_copy_wrapper_mrb *> &queue): - _queue(queue){/*NOP*/} + usb_zero_copy_wrapper_mrb(void){/*NOP*/} void release(void){ - if (not _mrb) return; _mrb.reset(); //decrement ref count, other MRB's may hold a ref - _queue.push_with_haste(this); + _claimer.release(); } - UHD_INLINE sptr get_new(managed_recv_buffer::sptr mrb, const void *mem, size_t len){ + UHD_INLINE sptr get_new( + managed_recv_buffer::sptr &mrb, size_t &offset_bytes, + const double timeout, size_t &index + ){ + if (not mrb or not _claimer.claim_with_wait(timeout)) return sptr(); + + index++; //advances the caller's buffer + + //hold a copy of the buffer shared pointer _mrb = mrb; - _mem = mem; - _len = len; - return make_managed_buffer(this); + + //extract this packet's memory address and length in bytes + char *mem = mrb->cast<char *>() + offset_bytes; + const boost::uint32_t *mem32 = reinterpret_cast<const boost::uint32_t *>(mem); + const size_t words32 = (uhd::wtohx(mem32[0]) & 0xffff); //length in words32 (from VRT header) + const size_t len = words32*sizeof(boost::uint32_t); //length in bytes + + //check if this receive buffer has been exhausted + offset_bytes += len; + if (offset_bytes >= mrb->size()) mrb.reset(); //drop caller's ref + else if (uhd::wtohx(mem32[words32]) == 0) mrb.reset(); + + return make(this, mem, len); } private: - const void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} - - bounded_buffer<usb_zero_copy_wrapper_mrb *> &_queue; - const void *_mem; - size_t _len; managed_recv_buffer::sptr _mrb; + simple_claimer _claimer; }; /*********************************************************************** @@ -63,16 +82,27 @@ private: class usb_zero_copy_wrapper_msb : public managed_send_buffer{ public: usb_zero_copy_wrapper_msb(const usb_zero_copy::sptr internal, const size_t fragmentation_size): - _internal(internal), _fragmentation_size(fragmentation_size){/*NOP*/} + _internal(internal), _fragmentation_size(fragmentation_size) + { + _ok_to_auto_flush = false; + _task = uhd::task::make(boost::bind(&usb_zero_copy_wrapper_msb::auto_flush, this)); + } - void commit(size_t len){ - if (len == 0) return; + ~usb_zero_copy_wrapper_msb(void) + { + //ensure the task has exited before anything auto deconstructs + _task.reset(); + } + + void release(void){ + boost::mutex::scoped_lock lock(_mutex); + _ok_to_auto_flush = true; //get a reference to the VITA header before incrementing const boost::uint32_t vita_header = reinterpret_cast<const boost::uint32_t *>(_mem_buffer_tip)[0]; - _bytes_in_buffer += len; - _mem_buffer_tip += len; + _bytes_in_buffer += size(); + _mem_buffer_tip += size(); //extract VITA end of packet flag, we must force flush under eof conditions const bool eop = (uhd::wtohx(vita_header) & (0x1 << 24)) != 0; @@ -80,28 +110,53 @@ public: if (eop or full){ _last_send_buff->commit(_bytes_in_buffer); _last_send_buff.reset(); + + //notify the auto-flusher to restart its timed_wait + lock.unlock(); _cond.notify_one(); } } UHD_INLINE sptr get_new(const double timeout){ + boost::mutex::scoped_lock lock(_mutex); + _ok_to_auto_flush = false; + if (not _last_send_buff){ _last_send_buff = _internal->get_send_buff(timeout); if (not _last_send_buff) return sptr(); _mem_buffer_tip = _last_send_buff->cast<char *>(); _bytes_in_buffer = 0; } - return make_managed_buffer(this); + + return make(this, _mem_buffer_tip, _fragmentation_size); } private: - void *get_buff(void) const{return reinterpret_cast<void *>(_mem_buffer_tip);} - size_t get_size(void) const{return _fragmentation_size;} - usb_zero_copy::sptr _internal; const size_t _fragmentation_size; managed_send_buffer::sptr _last_send_buff; size_t _bytes_in_buffer; char *_mem_buffer_tip; + + //private variables for auto flusher + boost::mutex _mutex; + boost::condition_variable _cond; + uhd::task::sptr _task; + bool _ok_to_auto_flush; + + /*! + * The auto flusher ensures that buffers are force committed when + * the user has not called get_new() within a certain time window. + */ + void auto_flush(void) + { + boost::mutex::scoped_lock lock(_mutex); + const bool timeout = not _cond.timed_wait(lock, AUTOFLUSH_TIMEOUT); + if (timeout and _ok_to_auto_flush and _last_send_buff and _bytes_in_buffer != 0) + { + _last_send_buff->commit(_bytes_in_buffer); + _last_send_buff.reset(); + } + } }; /*********************************************************************** @@ -112,44 +167,26 @@ public: usb_zero_copy_wrapper(sptr usb_zc, const size_t frame_boundary): _internal_zc(usb_zc), _frame_boundary(frame_boundary), - _available_recv_buffs(this->get_num_recv_frames()), - _mrb_pool(this->get_num_recv_frames(), usb_zero_copy_wrapper_mrb(_available_recv_buffs)), - _the_only_msb(usb_zero_copy_wrapper_msb(usb_zc, frame_boundary)) + _next_recv_buff_index(0) { - BOOST_FOREACH(usb_zero_copy_wrapper_mrb &mrb, _mrb_pool){ - _available_recv_buffs.push_with_haste(&mrb); + for (size_t i = 0; i < this->get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::make_shared<usb_zero_copy_wrapper_mrb>()); } + _the_only_msb = boost::make_shared<usb_zero_copy_wrapper_msb>(usb_zc, frame_boundary); } managed_recv_buffer::sptr get_recv_buff(double timeout){ //attempt to get a managed recv buffer - if (not _last_recv_buff.get()){ + if (not _last_recv_buff){ _last_recv_buff = _internal_zc->get_recv_buff(timeout); - _last_recv_offset = 0; + _last_recv_offset = 0; //reset offset into buffer } - //attempt to get a wrapper for a managed recv buffer - usb_zero_copy_wrapper_mrb *wmrb = NULL; - if (_last_recv_buff.get() and _available_recv_buffs.pop_with_timed_wait(wmrb, timeout)){ - //extract this packet's memory address and length in bytes - const char *mem = _last_recv_buff->cast<const char *>() + _last_recv_offset; - const boost::uint32_t *mem32 = reinterpret_cast<const boost::uint32_t *>(mem); - const size_t len = (uhd::wtohx(mem32[0]) & 0xffff)*sizeof(boost::uint32_t); //length in bytes (from VRT header) - - managed_recv_buffer::sptr recv_buff; //the buffer to be returned to the user - recv_buff = wmrb->get_new(_last_recv_buff, mem, len); - _last_recv_offset += len; - - //check if this receive buffer has been exhausted - if (_last_recv_offset >= _last_recv_buff->size()) { - _last_recv_buff.reset(); - } - - return recv_buff; - } - - //otherwise return a null sptr for failure - return managed_recv_buffer::sptr(); + //get the buffer to be returned to the user + if (_next_recv_buff_index == _mrb_pool.size()) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new( + _last_recv_buff, _last_recv_offset, timeout, _next_recv_buff_index + ); } size_t get_num_recv_frames(void) const{ @@ -161,7 +198,7 @@ public: } managed_send_buffer::sptr get_send_buff(double timeout){ - return _the_only_msb.get_new(timeout); + return _the_only_msb->get_new(timeout); } size_t get_num_send_frames(void) const{ @@ -175,16 +212,13 @@ public: private: sptr _internal_zc; size_t _frame_boundary; - bounded_buffer<usb_zero_copy_wrapper_mrb *> _available_recv_buffs; - std::vector<usb_zero_copy_wrapper_mrb> _mrb_pool; - usb_zero_copy_wrapper_msb _the_only_msb; - - //buffer to store partially-received VRT packets in - buffer_pool::sptr _fragment_mem; + std::vector<boost::shared_ptr<usb_zero_copy_wrapper_mrb> > _mrb_pool; + boost::shared_ptr<usb_zero_copy_wrapper_msb> _the_only_msb; //state for last recv buffer to create multiple managed buffers managed_recv_buffer::sptr _last_recv_buff; size_t _last_recv_offset; + size_t _next_recv_buff_index; }; /*********************************************************************** diff --git a/host/lib/types/ranges.cpp b/host/lib/types/ranges.cpp index 6e39bc688..82a9a84e1 100644 --- a/host/lib/types/ranges.cpp +++ b/host/lib/types/ranges.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -27,17 +27,8 @@ using namespace uhd; /*********************************************************************** * range_t implementation code **********************************************************************/ -struct range_t::impl{ - impl(double start, double stop, double step): - start(start), stop(stop), step(step) - { - /* NOP */ - } - double start, stop, step; -}; - range_t::range_t(double value): - _impl(UHD_PIMPL_MAKE(impl, (value, value, 0))) + _start(value), _stop(value), _step(0.0) { /* NOP */ } @@ -45,7 +36,7 @@ range_t::range_t(double value): range_t::range_t( double start, double stop, double step ): - _impl(UHD_PIMPL_MAKE(impl, (start, stop, step))) + _start(start), _stop(stop), _step(step) { if (stop < start){ throw uhd::value_error("cannot make range where stop < start"); @@ -53,15 +44,15 @@ range_t::range_t( } double range_t::start(void) const{ - return _impl->start; + return _start; } double range_t::stop(void) const{ - return _impl->stop; + return _stop; } double range_t::step(void) const{ - return _impl->step; + return _step; } const std::string range_t::to_pp_string(void) const{ diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index 1237f52d1..d2c33b512 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 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 @@ -26,7 +26,6 @@ LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) IF(ENABLE_B100) LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp diff --git a/host/lib/usrp/b100/b100_ctrl.cpp b/host/lib/usrp/b100/b100_ctrl.cpp deleted file mode 100644 index e6136c00e..000000000 --- a/host/lib/usrp/b100/b100_ctrl.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// -// 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 "b100_ctrl.hpp" -#include <uhd/transport/bounded_buffer.hpp> -#include <uhd/transport/usb_zero_copy.hpp> -#include <uhd/transport/zero_copy.hpp> -#include <uhd/transport/vrt_if_packet.hpp> -#include <uhd/utils/thread_priority.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/utils/tasks.hpp> -#include <uhd/types/metadata.hpp> -#include <uhd/types/serial.hpp> -#include "ctrl_packet.hpp" -#include <boost/thread/thread.hpp> -#include <boost/bind.hpp> -#include <uhd/exception.hpp> - -using namespace uhd::transport; -using namespace uhd; - -bool b100_ctrl_debug = false; - -class b100_ctrl_impl : public b100_ctrl { -public: - b100_ctrl_impl(uhd::transport::zero_copy_if::sptr ctrl_transport): - sync_ctrl_fifo(2), - _ctrl_transport(ctrl_transport), - _seq(0) - { - viking_marauder = task::make(boost::bind(&b100_ctrl_impl::viking_marauder_loop, this)); - } - - ~b100_ctrl_impl(void){ - //stop the marauder first so it cant access deconstructed objects - viking_marauder.reset(); - } - - int write(boost::uint32_t addr, const ctrl_data_t &data); - ctrl_data_t read(boost::uint32_t addr, size_t len); - - bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout); - - void poke32(wb_addr_type addr, boost::uint32_t data){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words(2); - words[0] = data & 0x0000FFFF; - words[1] = data >> 16; - this->write(addr, words); - } - - boost::uint32_t peek32(wb_addr_type addr){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words = this->read(addr, 2); - return boost::uint32_t((boost::uint32_t(words[1]) << 16) | words[0]); - } - - void poke16(wb_addr_type addr, boost::uint16_t data){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words(1); - words[0] = data; - this->write(addr, words); - } - - boost::uint16_t peek16(wb_addr_type addr){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words = this->read(addr, 1); - return boost::uint16_t(words[0]); - } - - void set_async_cb(const async_cb_type &async_cb){ - boost::mutex::scoped_lock lock(_async_mutex); - _async_cb = async_cb; - } - -private: - int send_pkt(boost::uint16_t *cmd); - - //änd hërë wë gö ä-Vïkïng för äsynchronous control packets - void viking_marauder_loop(void); - bounded_buffer<ctrl_data_t> sync_ctrl_fifo; - async_cb_type _async_cb; - task::sptr viking_marauder; - - uhd::transport::zero_copy_if::sptr _ctrl_transport; - boost::uint8_t _seq; - boost::mutex _ctrl_mutex, _async_mutex; -}; - -/*********************************************************************** - * helper functions for packing/unpacking control packets - **********************************************************************/ -void pack_ctrl_pkt(boost::uint16_t *pkt_buff, - const ctrl_pkt_t &pkt){ - //first two bits are OP - //next six bits are CALLBACKS - //next 8 bits are SEQUENCE - //next 16 bits are LENGTH (16-bit word) - //next 32 bits are ADDRESS (16-bit word LSW) - //then DATA (28 16-bit words) - pkt_buff[0] = (boost::uint16_t(pkt.pkt_meta.op) << 14) | (boost::uint16_t(pkt.pkt_meta.callbacks) << 8) | pkt.pkt_meta.seq; - pkt_buff[1] = pkt.pkt_meta.len; - pkt_buff[2] = (pkt.pkt_meta.addr & 0x00000FFF); - pkt_buff[3] = 0x0000; //address high bits always 0 on this device - - for(size_t i = 0; i < pkt.data.size(); i++) { - pkt_buff[4+i] = pkt.data[i]; - } -} - -void unpack_ctrl_pkt(const boost::uint16_t *pkt_buff, - ctrl_pkt_t &pkt){ - pkt.pkt_meta.seq = pkt_buff[0] & 0xFF; - pkt.pkt_meta.op = CTRL_PKT_OP_READ; //really this is useless - pkt.pkt_meta.len = pkt_buff[1]; - pkt.pkt_meta.callbacks = 0; //callbacks aren't implemented yet - pkt.pkt_meta.addr = pkt_buff[2] | boost::uint32_t(pkt_buff[3] << 16); - - //let's check this so we don't go pushing 64K of crap onto the pkt - if(pkt.pkt_meta.len > CTRL_PACKET_DATA_LENGTH) { - throw uhd::runtime_error("Received control packet too long"); - } - - for(int i = 4; i < 4+pkt.pkt_meta.len; i++) pkt.data.push_back(pkt_buff[i]); -} - -int b100_ctrl_impl::send_pkt(boost::uint16_t *cmd) { - managed_send_buffer::sptr sbuf = _ctrl_transport->get_send_buff(); - if(!sbuf.get()) { - throw uhd::runtime_error("Control channel send error"); - } - - //FIXME there's a better way to do this - for(size_t i = 0; i < (CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)); i++) { - sbuf->cast<boost::uint16_t *>()[i] = cmd[i]; - } - sbuf->commit(CTRL_PACKET_LENGTH); //fixed size transaction - return 0; -} - -int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) { - UHD_ASSERT_THROW(data.size() <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); - ctrl_pkt_t pkt; - pkt.data = data; - pkt.pkt_meta.op = CTRL_PKT_OP_WRITE; - pkt.pkt_meta.callbacks = 0; - pkt.pkt_meta.seq = _seq++; - pkt.pkt_meta.len = pkt.data.size(); - pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {}; - - pack_ctrl_pkt(pkt_buff, pkt); - size_t result = send_pkt(pkt_buff); - return result; -} - -ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) { - UHD_ASSERT_THROW(len <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); - - ctrl_pkt_t pkt; - pkt.pkt_meta.op = CTRL_PKT_OP_READ; - pkt.pkt_meta.callbacks = 0; - pkt.pkt_meta.seq = _seq++; - pkt.pkt_meta.len = len; - pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {}; - - //flush anything that might be in the queue - while (get_ctrl_data(pkt.data, 0.0)){ - UHD_MSG(error) << "B100: control read found unexpected packet." << std::endl; - } - - pack_ctrl_pkt(pkt_buff, pkt); - send_pkt(pkt_buff); - - //block with timeout waiting for the response to appear - if (not get_ctrl_data(pkt.data, 0.1)) throw uhd::runtime_error( - "B100: timeout waiting for control response packet." - ); - - return pkt.data; -} - -/*********************************************************************** - * Viking marauders go pillaging for asynchronous control packets in the - * control response endpoint. Sync packets go in sync_ctrl_fifo, - * async TX error messages go in async_msg_fifo. sync_ctrl_fifo should - * never have more than 1 message in it, since it's expected that we'll - * wait for a control operation to finish before starting another one. - **********************************************************************/ -void b100_ctrl_impl::viking_marauder_loop(void){ - set_thread_priority_safe(); - - while (not boost::this_thread::interruption_requested()){ - managed_recv_buffer::sptr rbuf = _ctrl_transport->get_recv_buff(1.0); - if(rbuf.get() == NULL) continue; //that's ok, there are plenty of villages to pillage! - const boost::uint16_t *pkt_buf = rbuf->cast<const boost::uint16_t *>(); - - if(pkt_buf[0] >> 8 == CTRL_PACKET_HEADER_MAGIC) { - //so it's got a control packet header, let's parse it. - ctrl_pkt_t pkt; - unpack_ctrl_pkt(pkt_buf, pkt); - - if(pkt.pkt_meta.seq != boost::uint8_t(_seq - 1)) { - UHD_MSG(error) - << "Sequence error on control channel." << std::endl - << "Exiting control loop." << std::endl - ; - return; - } - if(pkt.pkt_meta.len > (CTRL_PACKET_LENGTH - CTRL_PACKET_HEADER_LENGTH)) { - UHD_MSG(error) - << "Control channel packet length too long" << std::endl - << "Exiting control loop." << std::endl - ; - return; - } - - //push it onto the queue - sync_ctrl_fifo.push_with_pop_on_full(pkt.data); - } - else{ //otherwise let the async callback handle it - boost::mutex::scoped_lock lock(_async_mutex); - if (not _async_cb.empty()) _async_cb(rbuf); - } - } -} - -bool b100_ctrl_impl::get_ctrl_data(ctrl_data_t &pkt_data, double timeout){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return sync_ctrl_fifo.pop_with_timed_wait(pkt_data, timeout); -} - -/*********************************************************************** - * Public make function for b100_ctrl interface - **********************************************************************/ -b100_ctrl::sptr b100_ctrl::make(uhd::transport::zero_copy_if::sptr ctrl_transport){ - return sptr(new b100_ctrl_impl(ctrl_transport)); -} diff --git a/host/lib/usrp/b100/b100_ctrl.hpp b/host/lib/usrp/b100/b100_ctrl.hpp deleted file mode 100644 index 74884d525..000000000 --- a/host/lib/usrp/b100/b100_ctrl.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// 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 INCLUDED_B100_CTRL_HPP -#define INCLUDED_B100_CTRL_HPP - -#include "wb_iface.hpp" -#include <uhd/transport/usb_zero_copy.hpp> -#include <uhd/types/serial.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -#include "ctrl_packet.hpp" -#include <boost/function.hpp> - -class b100_ctrl : boost::noncopyable, public wb_iface{ -public: - typedef boost::shared_ptr<b100_ctrl> sptr; - typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> async_cb_type; - - /*! - * Make a USRP control object from a data transport - * \param ctrl_transport a USB data transport - * \return a new b100 control object - */ - static sptr make(uhd::transport::zero_copy_if::sptr ctrl_transport); - - //! set an async callback for messages - virtual void set_async_cb(const async_cb_type &async_cb) = 0; - - /*! - * Write a byte vector to an FPGA register - * \param addr the FPGA register address - * \param bytes the data to write - * \return 0 on success, error code on failure - */ - virtual int write(boost::uint32_t addr, const ctrl_data_t &data) = 0; - - /*! - * Read a byte vector from an FPGA register (blocking read) - * \param addr the FPGA register address - * \param len the length of the read - * \return a vector of bytes from the register(s) in question - */ - virtual ctrl_data_t read(boost::uint32_t addr, size_t len) = 0; - - /*! - * Get a sync ctrl packet (blocking) - * \param the packet data buffer - * \param the timeout value - * \return true if it got something - */ - virtual bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout) = 0; - -}; - -#endif /* INCLUDED_B100_CTRL_HPP */ diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 991e6efd3..a5a0ef9b0 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -17,12 +17,11 @@ #include "apply_corrections.hpp" #include "b100_impl.hpp" -#include "b100_ctrl.hpp" +#include "b100_regs.hpp" #include "fpga_regs_standard.h" #include "usrp_i2c_addr.h" #include "usrp_commands.h" #include <uhd/transport/usb_control.hpp> -#include "ctrl_packet.hpp" #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> @@ -33,8 +32,8 @@ #include <boost/filesystem.hpp> #include <boost/thread/thread.hpp> #include <boost/lexical_cast.hpp> -#include "b100_regs.hpp" #include <cstdio> +#include <iostream> using namespace uhd; using namespace uhd::usrp; @@ -85,13 +84,10 @@ static device_addrs_t b100_find(const device_addr_t &hint) b100_fw_image = find_image_path(hint.get("fw", B100_FW_FILE_NAME)); } catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate B100 firmware.\n" - "Please install the images package.\n" - ); + UHD_MSG(warning) << boost::format("Could not locate B100 firmware. %s\n") % print_images_error(); return b100_addrs; } - UHD_LOG << "the firmware image: " << b100_fw_image << std::endl; + UHD_LOG << "the firmware image: " << b100_fw_image << std::endl; usb_control::sptr control; try{control = usb_control::make(handle, 0);} @@ -117,7 +113,7 @@ static device_addrs_t b100_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, mboard_eeprom_t::MAP_B100); + const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, B100_EEPROM_MAP_KEY); device_addr_t new_addr; new_addr["type"] = "b100"; new_addr["name"] = mb_eeprom["name"]; @@ -190,10 +186,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //create the control transport device_addr_t ctrl_xport_args; - ctrl_xport_args["recv_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); + ctrl_xport_args["recv_frame_size"] = "512"; ctrl_xport_args["num_recv_frames"] = "16"; - ctrl_xport_args["send_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); - ctrl_xport_args["num_send_frames"] = "4"; + ctrl_xport_args["send_frame_size"] = "512"; + ctrl_xport_args["num_send_frames"] = "16"; _ctrl_transport = usb_zero_copy::make( handle, @@ -201,17 +197,22 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ 3, 4, //interface, endpoint ctrl_xport_args ); - while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport this->enable_gpif(true); //////////////////////////////////////////////////////////////////// - // Initialize FPGA wishbone communication + // Initialize FPGA control communication //////////////////////////////////////////////////////////////////// - _fpga_ctrl = b100_ctrl::make(_ctrl_transport); - _fpga_ctrl->poke32(B100_REG_GLOBAL_RESET, 0); //global fpga reset + fifo_ctrl_excelsior_config fifo_ctrl_config; + fifo_ctrl_config.async_sid_base = B100_TX_ASYNC_SID; + fifo_ctrl_config.num_async_chan = 1; + fifo_ctrl_config.ctrl_sid_base = B100_CTRL_MSG_SID; + fifo_ctrl_config.spi_base = TOREG(SR_SPI); + fifo_ctrl_config.spi_rb = REG_RB_SPI; + _fifo_ctrl = fifo_ctrl_excelsior::make(_ctrl_transport, fifo_ctrl_config); + //perform a test peek operation try{ - _fpga_ctrl->peek32(0); + _fifo_ctrl->peek32(0); } //try reset once in the case of failure catch(const uhd::exception &){ @@ -219,7 +220,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ UHD_MSG(warning) << "The control endpoint was left in a bad state.\n" "Attempting endpoint re-enumeration...\n" << std::endl; - _fpga_ctrl.reset(); + _fifo_ctrl.reset(); _ctrl_transport.reset(); _fx2_ctrl->usrp_fx2_reset(); goto b100_impl_constructor_begin; @@ -229,8 +230,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Initialize peripherals after reset //////////////////////////////////////////////////////////////////// - _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, B100_REG_SLAVE(3)); - _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, B100_REG_SLAVE(2)); + _fpga_i2c_ctrl = i2c_core_200::make(_fifo_ctrl, TOREG(SR_I2C), REG_RB_I2C); //////////////////////////////////////////////////////////////////// // Create data transport @@ -244,6 +244,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384"); data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + //let packet padder know the LUT size in number of words32 + const size_t rx_lut_size = size_t(data_xport_args.cast<double>("recv_frame_size", 0.0)); + _fifo_ctrl->poke32(TOREG(SR_PADDER+0), rx_lut_size/sizeof(boost::uint32_t)); + _data_transport = usb_zero_copy::make_wrapper( usb_zero_copy::make( handle, // identifier @@ -253,21 +257,21 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ ), B100_MAX_PKT_BYTE_LIMIT ); - while (_data_transport->get_recv_buff(0.0)){} //flush data xport //////////////////////////////////////////////////////////////////// // Initialize the properties tree //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("B-Series Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set("B100 (B-Hundo)"); + _tree->create<std::string>(mb_path / "name").set("B100"); + _tree->create<std::string>(mb_path / "codename").set("B-Hundo"); _tree->create<std::string>(mb_path / "load_eeprom") .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B100); + const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, B100_EEPROM_MAP_KEY); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&b100_impl::set_mb_eeprom, this, _1)); @@ -278,12 +282,17 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //^^^ clock created up top, just reg props here... ^^^ _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&b100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) .subscribe(boost::bind(&b100_impl::update_tick_rate, this, _1)); + //subscribe the command time while we are at it + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); + //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// - _codec_ctrl = b100_codec_ctrl::make(_fpga_spi_ctrl); + _codec_ctrl = b100_codec_ctrl::make(_fifo_ctrl); const fs_path rx_codec_path = mb_path / "rx_codecs/A"; const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); @@ -305,8 +314,8 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create frontend control objects //////////////////////////////////////////////////////////////////// - _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_FRONT)); - _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_FRONT)); + _rx_fe = rx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_RX_FE)); + _tx_fe = tx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_TX_FE)); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") .subscribe(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1)); @@ -335,13 +344,17 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP0), B100_REG_SR_ADDR(B100_SR_RX_CTRL0), B100_RX_SID_BASE + 0 - )); - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP1), B100_REG_SR_ADDR(B100_SR_RX_CTRL1), B100_RX_SID_BASE + 1 - )); - for (size_t dspno = 0; dspno < _rx_dsps.size(); dspno++){ + const size_t num_rx_dsps = _fifo_ctrl->peek32(REG_RB_NUM_RX_DSP); + for (size_t dspno = 0; dspno < num_rx_dsps; dspno++) + { + const size_t sr_off = dspno*32; + _rx_dsps.push_back(rx_dsp_core_200::make( + _fifo_ctrl, + TOREG(SR_RX_DSP0+sr_off), + TOREG(SR_RX_CTRL0+sr_off), + B100_RX_SID_BASE + dspno + )); + _rx_dsps[dspno]->set_link_rate(B100_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); @@ -364,7 +377,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////////// _tx_dsp = tx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_DSP), B100_REG_SR_ADDR(B100_SR_TX_CTRL), B100_TX_ASYNC_SID + _fifo_ctrl, TOREG(SR_TX_DSP), TOREG(SR_TX_CTRL), B100_TX_ASYNC_SID ); _tx_dsp->set_link_rate(B100_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -384,12 +397,12 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ // create time control objects //////////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_hi_now = B100_REG_RB_TIME_NOW_HI; - time64_rb_bases.rb_lo_now = B100_REG_RB_TIME_NOW_LO; - time64_rb_bases.rb_hi_pps = B100_REG_RB_TIME_PPS_HI; - time64_rb_bases.rb_lo_pps = B100_REG_RB_TIME_PPS_LO; + time64_rb_bases.rb_hi_now = REG_RB_TIME_NOW_HI; + time64_rb_bases.rb_lo_now = REG_RB_TIME_NOW_LO; + time64_rb_bases.rb_hi_pps = REG_RB_TIME_PPS_HI; + time64_rb_bases.rb_lo_pps = REG_RB_TIME_PPS_LO; _time64 = time64_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TIME64), time64_rb_bases + _fifo_ctrl, TOREG(SR_TIME64), time64_rb_bases ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1)); @@ -413,7 +426,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _user = user_settings_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_USER_REGS)); + _user = user_settings_core_200::make(_fifo_ctrl, TOREG(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); @@ -427,6 +440,9 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ tx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_A); gdb_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_A ^ 5); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(rx_fe_path / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom") .set(rx_db_eeprom) @@ -439,7 +455,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "gdb", _1)); //create a new dboard interface and manager - _dboard_iface = make_b100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl); + _dboard_iface = make_b100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface); _dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -459,7 +475,11 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ } //initialize io handling - this->io_init(); + _recv_demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); + + //allocate streamer weak ptrs containers + _rx_streamers.resize(_rx_dsps.size()); + _tx_streamers.resize(1/*known to be 1 dsp*/); //////////////////////////////////////////////////////////////////// // do some post-init tasks @@ -484,8 +504,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ } b100_impl::~b100_impl(void){ - //set an empty async callback now that we deconstruct - _fpga_ctrl->set_async_cb(b100_ctrl::async_cb_type()); + //NOP } void b100_impl::check_fw_compat(void){ @@ -496,13 +515,14 @@ void b100_impl::check_fw_compat(void){ if (fw_compat_num != B100_FW_COMPAT_NUM){ throw uhd::runtime_error(str(boost::format( "Expected firmware compatibility number 0x%x, but got 0x%x:\n" - "The firmware build is not compatible with the host code build." - ) % B100_FW_COMPAT_NUM % fw_compat_num)); + "The firmware build is not compatible with the host code build.\n" + "%s" + ) % B100_FW_COMPAT_NUM % fw_compat_num % print_images_error())); } } void b100_impl::check_fpga_compat(void){ - const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(B100_REG_RB_COMPAT); + const boost::uint32_t fpga_compat_num = _fifo_ctrl->peek32(REG_RB_COMPAT); boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; if (fpga_major == 0){ //old version scheme fpga_major = fpga_minor; @@ -512,7 +532,8 @@ void b100_impl::check_fpga_compat(void){ throw uhd::runtime_error(str(boost::format( "Expected FPGA compatibility number %d, but got %d:\n" "The FPGA build is not compatible with the host code build." - ) % int(B100_FPGA_COMPAT_NUM) % fpga_major)); + "%s" + ) % int(B100_FPGA_COMPAT_NUM) % fpga_major % print_images_error())); } _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); } @@ -526,7 +547,7 @@ double b100_impl::update_rx_codec_gain(const double gain){ } void b100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B100); + mb_eeprom.commit(*_fx2_ctrl, B100_EEPROM_MAP_KEY); } void b100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -536,6 +557,19 @@ void b100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_e } void b100_impl::update_clock_source(const std::string &source){ + + if (source == "pps_sync"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 1); + return; + } + if (source == "_pps_sync_"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 3); + return; + } + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 0); + if (source == "auto") _clock_ctrl->use_auto_ref(); else if (source == "internal") _clock_ctrl->use_internal_ref(); else if (source == "external") _clock_ctrl->use_external_ref(); diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index eab9c750b..250229fb8 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -19,20 +19,19 @@ #define INCLUDED_B100_IMPL_HPP #include "fx2_ctrl.hpp" -#include "b100_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" -#include "spi_core_100.hpp" -#include "i2c_core_100.hpp" +#include "i2c_core_200.hpp" #include "rx_frontend_core_200.hpp" #include "tx_frontend_core_200.hpp" #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "fifo_ctrl_excelsior.hpp" #include "user_settings_core_200.hpp" +#include "recv_packet_demuxer.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> -#include <uhd/utils/pimpl.hpp> #include <uhd/types/dict.hpp> #include <uhd/types/sensors.hpp> #include <uhd/types/clock_config.hpp> @@ -47,12 +46,14 @@ static const double B100_LINK_RATE_BPS = 256e6/5; //pratical link rate (< 480 Mbps) static const std::string B100_FW_FILE_NAME = "usrp_b100_fw.ihx"; static const std::string B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin"; -static const boost::uint16_t B100_FW_COMPAT_NUM = 0x03; -static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x09; -static const boost::uint32_t B100_RX_SID_BASE = 2; -static const boost::uint32_t B100_TX_ASYNC_SID = 1; +static const boost::uint16_t B100_FW_COMPAT_NUM = 0x04; +static const boost::uint16_t B100_FPGA_COMPAT_NUM = 11; +static const boost::uint32_t B100_RX_SID_BASE = 30; +static const boost::uint32_t B100_TX_ASYNC_SID = 10; +static const boost::uint32_t B100_CTRL_MSG_SID = 20; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; +static const std::string B100_EEPROM_MAP_KEY = "B100"; //! Make a b100 dboard interface uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( @@ -79,8 +80,8 @@ private: uhd::property_tree::sptr _tree; //controllers - spi_core_100::sptr _fpga_spi_ctrl; - i2c_core_100::sptr _fpga_i2c_ctrl; + fifo_ctrl_excelsior::sptr _fifo_ctrl; + i2c_core_200::sptr _fpga_i2c_ctrl; rx_frontend_core_200::sptr _rx_fe; tx_frontend_core_200::sptr _tx_fe; std::vector<rx_dsp_core_200::sptr> _rx_dsps; @@ -89,20 +90,17 @@ private: user_settings_core_200::sptr _user; b100_clock_ctrl::sptr _clock_ctrl; b100_codec_ctrl::sptr _codec_ctrl; - b100_ctrl::sptr _fpga_ctrl; uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; //transports - uhd::transport::zero_copy_if::sptr _data_transport, _ctrl_transport; + uhd::transport::zero_copy_if::sptr _ctrl_transport; + uhd::transport::zero_copy_if::sptr _data_transport; + uhd::usrp::recv_packet_demuxer::sptr _recv_demuxer; //dboard stuff uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //handle io stuff - UHD_PIMPL_DECL(io_impl) _io_impl; - void io_init(void); - //device properties interface uhd::property_tree::sptr get_tree(void) const{ return _tree; @@ -125,7 +123,6 @@ private: void update_clock_source(const std::string &); void enable_gpif(const bool); void clear_fpga_fifo(void); - void handle_async_message(uhd::transport::managed_recv_buffer::sptr); uhd::sensor_value_t get_ref_locked(void); void set_rx_fe_corrections(const double); void set_tx_fe_corrections(const double); diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp index 987a09f03..48eb0460d 100644 --- a/host/lib/usrp/b100/b100_regs.hpp +++ b/host/lib/usrp/b100/b100_regs.hpp @@ -15,109 +15,49 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -//////////////////////////////////////////////////////////////// -// -// Memory map for wishbone bus -// -//////////////////////////////////////////////////////////////// - -// All addresses are byte addresses. All accesses are word (16-bit) accesses. -// This means that address bit 0 is usually 0. -// There are 11 bits of address for the control. - #ifndef INCLUDED_B100_REGS_HPP #define INCLUDED_B100_REGS_HPP -///////////////////////////////////////////////////// -// Slave pointers +#include <boost/cstdint.hpp> -#define B100_REG_SLAVE(n) ((n)<<7) +#define TOREG(x) ((x)*4) -///////////////////////////////////////////////////// -// Slave 0 -- Misc Regs +#define localparam static const int -#define B100_REG_MISC_BASE B100_REG_SLAVE(0) +localparam SR_MISC = 0; // 5 +localparam SR_USER_REGS = 5; // 2 +localparam SR_PADDER = 10; // 2 -#define B100_REG_MISC_LED B100_REG_MISC_BASE + 0 -#define B100_REG_MISC_SW B100_REG_MISC_BASE + 2 -#define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4 -#define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6 +localparam SR_TX_CTRL = 32; // 6 +localparam SR_TX_DSP = 40; // 5 +localparam SR_TX_FE = 48; // 5 -///////////////////////////////////////////////////// -// Slave 1 -- UART -// CLKDIV is 16 bits, others are only 8 +localparam SR_RX_CTRL0 = 96; // 9 +localparam SR_RX_DSP0 = 106; // 7 +localparam SR_RX_FE = 114; // 5 -#define B100_REG_UART_BASE B100_REG_SLAVE(1) +localparam SR_RX_CTRL1 = 128; // 9 +localparam SR_RX_DSP1 = 138; // 7 -#define B100_REG_UART_CLKDIV B100_REG_UART_BASE + 0 -#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2 -#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4 -#define B100_REG_UART_TXCHAR B100_REG_UART_BASE + 6 -#define B100_REG_UART_RXCHAR B100_REG_UART_BASE + 8 +localparam SR_TIME64 = 192; // 6 +localparam SR_SPI = 208; // 3 +localparam SR_I2C = 216; // 1 +localparam SR_GPIO = 224; // 5 -///////////////////////////////////////////////////// -// Slave 2 -- SPI Core -//these are 32-bit registers mapped onto the 16-bit Wishbone bus. -//Using peek32/poke32 should allow transparent use of these registers. -#define B100_REG_SPI_BASE B100_REG_SLAVE(2) +#define REG_RB_TIME_NOW_HI TOREG(10) +#define REG_RB_TIME_NOW_LO TOREG(11) +#define REG_RB_TIME_PPS_HI TOREG(14) +#define REG_RB_TIME_PPS_LO TOREG(15) +#define REG_RB_SPI TOREG(0) +#define REG_RB_COMPAT TOREG(1) +#define REG_RB_GPIO TOREG(3) +#define REG_RB_I2C TOREG(2) +#define REG_RB_NUM_RX_DSP TOREG(6) //spi slave constants #define B100_SPI_SS_AD9862 (1 << 2) #define B100_SPI_SS_TX_DB (1 << 1) #define B100_SPI_SS_RX_DB (1 << 0) -//////////////////////////////////////////////// -// Slave 3 -- I2C Core - -#define B100_REG_I2C_BASE B100_REG_SLAVE(3) - -/////////////////////////////////////////////////// -// Slave 7 -- Readback Mux 32 - -#define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7) - -#define B100_REG_RB_TIME_NOW_HI B100_REG_RB_MUX_32_BASE + 0 -#define B100_REG_RB_TIME_NOW_LO B100_REG_RB_MUX_32_BASE + 4 -#define B100_REG_RB_TIME_PPS_HI B100_REG_RB_MUX_32_BASE + 8 -#define B100_REG_RB_TIME_PPS_LO B100_REG_RB_MUX_32_BASE + 12 -#define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 -#define B100_REG_RB_COMPAT B100_REG_RB_MUX_32_BASE + 24 -#define B100_REG_RB_GPIO B100_REG_RB_MUX_32_BASE + 28 - -//////////////////////////////////////////////////// -// Slaves 8 & 9 -- Settings Bus -// -// Output-only, no readback, 64 registers total -// Each register must be written 32 bits at a time -// First the address xxx_xx00 and then xxx_xx10 -// 64 total regs in address space -#define B100_SR_RX_CTRL0 0 // 9 regs (+0 to +8) -#define B100_SR_RX_DSP0 10 // 4 regs (+0 to +3) -#define B100_SR_RX_CTRL1 16 // 9 regs (+0 to +8) -#define B100_SR_RX_DSP1 26 // 4 regs (+0 to +3) -#define B100_SR_TX_CTRL 32 // 4 regs (+0 to +3) -#define B100_SR_TX_DSP 38 // 3 regs (+0 to +2) - -#define B100_SR_TIME64 42 // 6 regs (+0 to +5) -#define B100_SR_RX_FRONT 48 // 5 regs (+0 to +4) -#define B100_SR_TX_FRONT 54 // 5 regs (+0 to +4) - -#define B100_SR_REG_TEST32 60 // 1 reg -#define B100_SR_CLEAR_FIFO 61 // 1 reg -#define B100_SR_GLOBAL_RESET 63 // 1 reg -#define B100_SR_USER_REGS 64 // 2 regs - -#define B100_SR_GPIO 128 - -#define B100_REG_SR_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) - -#define B100_REG_SR_MISC_TEST32 B100_REG_SR_ADDR(B100_SR_REG_TEST32) - -///////////////////////////////////////////////// -// Magic reset regs -//////////////////////////////////////////////// -#define B100_REG_CLEAR_FIFO B100_REG_SR_ADDR(B100_SR_CLEAR_FIFO) -#define B100_REG_GLOBAL_RESET B100_REG_SR_ADDR(B100_SR_GLOBAL_RESET) - -#endif +#endif /*INCLUDED_B100_REGS_HPP*/ diff --git a/host/lib/usrp/b100/ctrl_packet.hpp b/host/lib/usrp/b100/ctrl_packet.hpp deleted file mode 100644 index bab1f0de1..000000000 --- a/host/lib/usrp/b100/ctrl_packet.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// 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 INCLUDED_CTRL_PACKET_HPP -#define INCLUDED_CTRL_PACKET_HPP - -#include <uhd/config.hpp> -#include <boost/cstdint.hpp> -#include <uhd/types/serial.hpp> - -typedef std::vector<boost::uint16_t> ctrl_data_t; - -/*! - * Control packet operation type - */ -enum ctrl_pkt_op_t { - CTRL_PKT_OP_WRITE = 1, - CTRL_PKT_OP_READ = 2, - CTRL_PKT_OP_READBACK = 3 -}; - -/*! - * Control packet transaction length - */ -const size_t CTRL_PACKET_LENGTH = 32; -const size_t CTRL_PACKET_HEADER_LENGTH = 8; -const size_t CTRL_PACKET_DATA_LENGTH = 24; //=length-header - -/*! - * Control packet header magic value - */ -const boost::uint8_t CTRL_PACKET_HEADER_MAGIC = 0xAA; - -/*! - * Callback triggers for readback operation - */ -//FIXME: these are not real numbers, callbacks aren't implemented yet -const boost::uint16_t CTRL_PACKET_CALLBACK_SPI = 0x0001; -const boost::uint16_t CTRL_PACKET_CALLBACK_I2C = 0x0002; -//and so on - -/*! - * Metadata structure to describe a control packet - */ -struct UHD_API ctrl_pkt_meta_t { - ctrl_pkt_op_t op; - boost::uint8_t callbacks; - boost::uint8_t seq; - boost::uint16_t len; - boost::uint32_t addr; -}; - -/*! - * Full control packet structure - */ -struct UHD_API ctrl_pkt_t { - ctrl_pkt_meta_t pkt_meta; - ctrl_data_t data; -}; - -#endif /* INCLUDED_CTRL_PACKET_HPP */ diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp index 39ad5c5ac..25604da72 100644 --- a/host/lib/usrp/b100/dboard_iface.cpp +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -45,7 +45,7 @@ public: _spi_iface = spi_iface; _clock = clock; _codec = codec; - _gpio = gpio_core_200::make(_wb_iface, B100_REG_SR_ADDR(B100_SR_GPIO), B100_REG_RB_GPIO); + _gpio = gpio_core_200::make(_wb_iface, TOREG(SR_GPIO), REG_RB_GPIO); //init the clock rate shadows this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp index 674380cca..723756dcc 100644 --- a/host/lib/usrp/b100/io_impl.cpp +++ b/host/lib/usrp/b100/io_impl.cpp @@ -15,16 +15,10 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "recv_packet_demuxer.hpp" #include "validate_subdev_spec.hpp" -#include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" -#include "usrp_commands.h" #include "b100_impl.hpp" -#include "b100_regs.hpp" -#include <uhd/utils/thread_priority.hpp> -#include <uhd/transport/bounded_buffer.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> #include <boost/bind.hpp> @@ -37,66 +31,6 @@ using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; -/*********************************************************************** - * IO Implementation Details - **********************************************************************/ -struct b100_impl::io_impl{ - io_impl(void): - async_msg_fifo(1000/*messages deep*/) - { /* NOP */ } - - zero_copy_if::sptr data_transport; - bounded_buffer<async_metadata_t> async_msg_fifo; - recv_packet_demuxer::sptr demuxer; - double tick_rate; -}; - -/*********************************************************************** - * Initialize internals within this file - **********************************************************************/ -void b100_impl::io_init(void){ - - //clear fifo state machines - _fpga_ctrl->poke32(B100_REG_CLEAR_FIFO, 0); - - //allocate streamer weak ptrs containers - _rx_streamers.resize(_rx_dsps.size()); - _tx_streamers.resize(1/*known to be 1 dsp*/); - - //create new io impl - _io_impl = UHD_PIMPL_MAKE(io_impl, ()); - _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); - - //now its safe to register the async callback - _fpga_ctrl->set_async_cb(boost::bind(&b100_impl::handle_async_message, this, _1)); -} - -void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){ - vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t); - const boost::uint32_t *vrt_hdr = rbuf->cast<const boost::uint32_t *>(); - try{ - vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); - } - catch(const std::exception &e){ - UHD_MSG(error) << "Error (handle_async_message): " << e.what() << std::endl; - } - - if (if_packet_info.sid == B100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ - - //fill in the async metadata - async_metadata_t metadata; - load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, vrt_hdr, _io_impl->tick_rate); - - //push the message onto the queue - _io_impl->async_msg_fifo.push_with_pop_on_full(metadata); - - //print some fastpath messages - standard_async_msg_prints(metadata); - } - else UHD_MSG(error) << "Unknown async packet" << std::endl; -} - void b100_impl::update_rates(void){ const fs_path mb_path = "/mboards/0"; _tree->access<double>(mb_path / "tick_rate").update(); @@ -111,7 +45,6 @@ void b100_impl::update_rates(void){ } void b100_impl::update_tick_rate(const double rate){ - _io_impl->tick_rate = rate; //update the tick rate on all existing streamers -> thread safe for (size_t i = 0; i < _rx_streamers.size(); i++){ @@ -181,8 +114,7 @@ void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ bool b100_impl::recv_async_msg( async_metadata_t &async_metadata, double timeout ){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); + return _fifo_ctrl->pop_async_msg(async_metadata, timeout); } /*********************************************************************** @@ -227,7 +159,7 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 + &recv_packet_demuxer::get_recv_buff, _recv_demuxer, dsp, _1 ), true /*flush*/); my_streamer->set_overflow_handler(chan_i, boost::bind( &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 9abd34afa..fa07e3d1d 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 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 @@ -32,4 +32,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp ) diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp index b889266f2..3d33e7d11 100644 --- a/host/lib/usrp/common/apply_corrections.cpp +++ b/host/lib/usrp/common/apply_corrections.cpp @@ -54,6 +54,12 @@ static bool fe_cal_comp(fe_cal_t a, fe_cal_t b){ static uhd::dict<std::string, std::vector<fe_cal_t> > fe_cal_cache; +static bool is_same_freq(const double f1, const double f2) +{ + const double epsilon = 0.1; + return ((f1 - epsilon) < f2 and (f1 + epsilon) > f2); +} + static std::complex<double> get_fe_correction( const std::string &key, const double lo_freq ){ @@ -64,6 +70,12 @@ static std::complex<double> get_fe_correction( size_t lo_index = 0; size_t hi_index = datas.size()-1; for (size_t i = 0; i < datas.size(); i++){ + if (is_same_freq(datas[i].lo_freq, lo_freq)) + { + hi_index = i; + lo_index = i; + break; + } if (datas[i].lo_freq > lo_freq){ hi_index = i; break; diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.cpp b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp new file mode 100644 index 000000000..5e4a1e243 --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp @@ -0,0 +1,293 @@ +// +// Copyright 2012 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 "fifo_ctrl_excelsior.hpp" +#include "async_packet_handler.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/bind.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const size_t POKE32_CMD = (1 << 8); +static const size_t PEEK32_CMD = 0; +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const boost::uint32_t MAX_SEQS_OUT = 15; + +#define SPI_DIV _config.spi_base + 0 +#define SPI_CTRL _config.spi_base + 4 +#define SPI_DATA _config.spi_base + 8 +#define SPI_DIVIDER 4 + +struct ctrl_result_t{ + boost::uint32_t msg[2]; +}; + +class fifo_ctrl_excelsior_impl : public fifo_ctrl_excelsior{ +public: + + fifo_ctrl_excelsior_impl(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config): + _xport(xport), + _config(config), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT), + _async_fifo(1000), + _ctrl_fifo(MAX_SEQS_OUT+1) + { + while (_xport->get_recv_buff(0.0)){} //flush + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + _msg_task = task::make(boost::bind(&fifo_ctrl_excelsior_impl::handle_msg, this)); + this->init_spi(); + } + + ~fifo_ctrl_excelsior_impl(void){ + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + ) + } + + bool pop_async_msg(async_metadata_t &async_metadata, double timeout){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return _async_fifo.pop_with_timed_wait(async_metadata, timeout); + } + + void handle_msg(void){ + set_thread_priority_safe(); + while (not boost::this_thread::interruption_requested()){ + this->handle_msg1(); + } + } + + void handle_msg1(void){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(); + if (not buff) return; + const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + try{ + vrt::if_hdr_unpack_le(pkt, packet_info); + } + catch(const std::exception &ex){ + UHD_MSG(error) << "FIFO ctrl bad VITA packet: " << ex.what() << std::endl; + } + if (packet_info.has_sid and packet_info.sid == _config.ctrl_sid_base){ + ctrl_result_t res = ctrl_result_t(); + res.msg[0] = uhd::wtohx(pkt[packet_info.num_header_words32+0]); + res.msg[1] = uhd::wtohx(pkt[packet_info.num_header_words32+1]); + _ctrl_fifo.push_with_haste(res); + } + else if (packet_info.has_sid and packet_info.sid >= _config.async_sid_base and packet_info.sid <= _config.async_sid_base + _config.num_async_chan){ + async_metadata_t metadata; + load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, packet_info, pkt, _tick_rate, packet_info.sid - _config.async_sid_base); + _async_fifo.push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } + else{ + UHD_MSG(error) << "FIFO ctrl got unknown SID: " << packet_info.sid << std::endl; + } + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(wb_addr_type addr, boost::uint32_t data){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, data, POKE32_CMD); + + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + } + + boost::uint32_t peek32(wb_addr_type addr){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, 0, PEEK32_CMD); + + return this->wait_for_ack(_seq_out); + } + + /******************************************************************* + * Peek and poke 16 bit not implemented + ******************************************************************/ + void poke16(wb_addr_type, boost::uint16_t){ + throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module"); + } + + boost::uint16_t peek16(wb_addr_type){ + throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module"); + } + + /******************************************************************* + * FIFO controlled SPI implementation + ******************************************************************/ + void init_spi(void){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + _ctrl_word_cache = 0; // force update first time around + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3ff) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word){ + this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + _ctrl_word_cache = ctrl_word; + } + + //send data word + this->send_pkt(SPI_DATA, data_out, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + //conditional readback + if (readback){ + this->send_pkt(_config.spi_rb, 0, PEEK32_CMD); + return this->wait_for_ack(_seq_out); + } + + return 0; + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time){ + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate){ + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){ + managed_send_buffer::sptr buff = _xport->get_send_buff(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = ++_seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.has_sid = false; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + vrt::if_hdr_pack_le(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr/4 & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = uhd::htowx(ctrl_word); + pkt[packet_info.num_header_words32+1] = uhd::htowx(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); + } + + UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){ + if (((i0 ^ i1) & 0x8000) == 0) //same sign bits + return boost::uint16_t(i0) < boost::uint16_t(i1); + return boost::int16_t(i1 - i0) > 0; + } + + UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){ + + while (wraparound_lt16(_seq_ack, seq_to_ack)){ + ctrl_result_t res = ctrl_result_t(); + if (not _ctrl_fifo.pop_with_timed_wait(res, _timeout)){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + _seq_ack = res.msg[0] >> 16; + if (_seq_ack == seq_to_ack) return res.msg[1]; + } + + return 0; + } + + zero_copy_if::sptr _xport; + const fifo_ctrl_excelsior_config _config; + boost::mutex _mutex; + boost::uint16_t _seq_out; + boost::uint16_t _seq_ack; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + boost::uint32_t _ctrl_word_cache; + bounded_buffer<async_metadata_t> _async_fifo; + bounded_buffer<ctrl_result_t> _ctrl_fifo; + task::sptr _msg_task; +}; + + +fifo_ctrl_excelsior::sptr fifo_ctrl_excelsior::make(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config) +{ + return sptr(new fifo_ctrl_excelsior_impl(xport, config)); +} diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp new file mode 100644 index 000000000..c3ef65a2c --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp @@ -0,0 +1,63 @@ +// +// Copyright 2012 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 INCLUDED_B200_CTRL_HPP +#define INCLUDED_B200_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + + +struct fifo_ctrl_excelsior_config +{ + size_t async_sid_base; + size_t num_async_chan; + size_t ctrl_sid_base; + size_t spi_base; + size_t spi_rb; +}; + +/*! + * Provide access to peek, poke, spi, and async messages. + */ +class fifo_ctrl_excelsior : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr; + + //! Make a new control object + static sptr make( + uhd::transport::zero_copy_if::sptr xport, + const fifo_ctrl_excelsior_config &config + ); + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; + + //! Pop an async message from the queue or timeout + virtual bool pop_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; +}; + +#endif /* INCLUDED_B200_CTRL_HPP */ diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp index 7b8920eb1..5cc701eb0 100644 --- a/host/lib/usrp/common/fx2_ctrl.cpp +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -411,6 +411,26 @@ public: return usrp_control_write(request, value, index, 0, 0); } + void write_eeprom( + boost::uint8_t addr, + boost::uint8_t offset, + const byte_vector_t &bytes + ){ + byte_vector_t bytes_with_cmd(bytes.size() + 1); + bytes_with_cmd[0] = offset; + std::copy(bytes.begin(), bytes.end(), &bytes_with_cmd[1]); + this->write_i2c(addr, bytes_with_cmd); + } + + byte_vector_t read_eeprom( + boost::uint8_t addr, + boost::uint8_t offset, + size_t num_bytes + ){ + this->write_i2c(addr, byte_vector_t(1, offset)); + return this->read_i2c(addr, num_bytes); + } + int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len) { return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len); @@ -428,12 +448,7 @@ public: { UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - std::copy(bytes.begin(), bytes.end(), buff); - - int ret = this->usrp_i2c_write(addr & 0xff, - buff, - bytes.size()); + int ret = this->usrp_i2c_write(addr, (unsigned char *)&bytes.front(), bytes.size()); if (iface_debug && (ret < 0)) uhd::runtime_error("USRP: failed i2c write"); @@ -443,19 +458,13 @@ public: { UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - int ret = this->usrp_i2c_read(addr & 0xff, - buff, - num_bytes); + byte_vector_t bytes(num_bytes); + int ret = this->usrp_i2c_read(addr, (unsigned char *)&bytes.front(), num_bytes); if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) uhd::runtime_error("USRP: failed i2c read"); - byte_vector_t out_bytes; - for (size_t i = 0; i < num_bytes; i++) - out_bytes.push_back(buff[i]); - - return out_bytes; + return bytes; } diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index aa5f0bcbb..3192b0774 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_100.cpp ${CMAKE_CURRENT_SOURCE_DIR}/time64_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_200.cpp diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index d756097ff..cdab70b8d 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -63,6 +63,7 @@ private: wb_iface::sptr _iface; const size_t _base; const size_t _rb_addr; + uhd::dict<size_t, boost::uint32_t> _update_cache; uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs; @@ -90,7 +91,12 @@ private: const boost::uint32_t ctrl = (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); - _iface->poke32(addr, (ctrl & atr_val) | ((~ctrl) & gpio_val)); + const boost::uint32_t val = (ctrl & atr_val) | ((~ctrl) & gpio_val); + if (not _update_cache.has_key(addr) or _update_cache[addr] != val) + { + _iface->poke32(addr, val); + } + _update_cache[addr] = val; } }; diff --git a/host/lib/usrp/cores/i2c_core_200.cpp b/host/lib/usrp/cores/i2c_core_200.cpp new file mode 100644 index 000000000..1b882c54a --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_200.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2011-2012 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 "i2c_core_200.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <boost/thread/mutex.hpp> + +#define REG_I2C_WR_PRESCALER_LO (1 << 3) | 0 +#define REG_I2C_WR_PRESCALER_HI (1 << 3) | 1 +#define REG_I2C_WR_CTRL (1 << 3) | 2 +#define REG_I2C_WR_DATA (1 << 3) | 3 +#define REG_I2C_WR_CMD (1 << 3) | 4 +#define REG_I2C_RD_DATA (0 << 3) | 3 +#define REG_I2C_RD_ST (0 << 3) | 4 + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + +using namespace uhd; + +class i2c_core_200_impl : public i2c_core_200{ +public: + i2c_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t readback): + _iface(iface), _base(base), _readback(readback) + { + //init I2C FPGA interface. + this->poke(REG_I2C_WR_CTRL, 0x0000); + //set prescalers to operate at 400kHz: WB_CLK is 64MHz... + static const boost::uint32_t i2c_datarate = 400000; + static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else + boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; + this->poke(REG_I2C_WR_PRESCALER_LO, prescaler & 0xFF); + this->poke(REG_I2C_WR_PRESCALER_HI, (prescaler >> 8) & 0xFF); + this->poke(REG_I2C_WR_CTRL, I2C_CTRL_EN); //enable I2C core + } + + void write_i2c( + boost::uint8_t addr, + const byte_vector_t &bytes + ){ + this->poke(REG_I2C_WR_DATA, (addr << 1) | 0); //addr and read bit (0) + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + + //wait for previous transfer to complete + if (not wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + return; + } + + for (size_t i = 0; i < bytes.size(); i++) { + this->poke(REG_I2C_WR_DATA, bytes[i]); + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); + if(!wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + return; + } + } + } + + byte_vector_t read_i2c( + boost::uint8_t addr, + size_t num_bytes + ){ + byte_vector_t bytes; + if (num_bytes == 0) return bytes; + + while (this->peek(REG_I2C_RD_ST) & I2C_ST_BUSY){ + /* NOP */ + } + + this->poke(REG_I2C_WR_DATA, (addr << 1) | 1); //addr and read bit (1) + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START); + //wait for previous transfer to complete + if (not wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + } + for (size_t i = 0; i < num_bytes; i++) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); + i2c_wait(); + bytes.push_back(this->peek(REG_I2C_RD_DATA)); + } + return bytes; + } + +private: + void i2c_wait(void) { + for (size_t i = 0; i < 100; i++){ + if ((this->peek(REG_I2C_RD_ST) & I2C_ST_TIP) == 0) return; + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + UHD_MSG(error) << "i2c_core_200: i2c_wait timeout" << std::endl; + } + + bool wait_chk_ack(void){ + i2c_wait(); + return (this->peek(REG_I2C_RD_ST) & I2C_ST_RXACK) == 0; + } + + void poke(const size_t what, const boost::uint8_t cmd) + { + boost::mutex::scoped_lock lock(_mutex); + _iface->poke32(_base, (what << 8) | cmd); + } + + boost::uint8_t peek(const size_t what) + { + boost::mutex::scoped_lock lock(_mutex); + _iface->poke32(_base, what << 8); + return boost::uint8_t(_iface->peek32(_readback)); + } + + wb_iface::sptr _iface; + const size_t _base; + const size_t _readback; + boost::mutex _mutex; +}; + +i2c_core_200::sptr i2c_core_200::make(wb_iface::sptr iface, const size_t base, const size_t readback){ + return sptr(new i2c_core_200_impl(iface, base, readback)); +} diff --git a/host/lib/usrp/cores/i2c_core_200.hpp b/host/lib/usrp/cores/i2c_core_200.hpp new file mode 100644 index 000000000..508855985 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_200.hpp @@ -0,0 +1,35 @@ +// +// Copyright 2011-2012 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 INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class i2c_core_200 : boost::noncopyable, public uhd::i2c_iface{ +public: + typedef boost::shared_ptr<i2c_core_200> sptr; + + //! makes a new i2c core from iface and slave base + static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback); +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index b73baa81e..ef6b85de9 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -19,6 +19,7 @@ #include <uhd/types/dict.hpp> #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> #include <uhd/utils/algorithm.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread/thread.hpp> //thread sleep @@ -76,8 +77,17 @@ public: this->clear(); } + ~rx_dsp_core_200_impl(void) + { + UHD_SAFE_CALL + ( + //shutdown any possible streaming + this->clear(); + ) + } + void clear(void){ - _iface->poke32(REG_RX_CTRL_NCHANNELS, 1); //also reset + _iface->poke32(REG_RX_CTRL_NCHANNELS, 0); //also reset _iface->poke32(REG_RX_CTRL_VRT_HDR, 0 | (0x1 << 28) //if data with stream id | (0x1 << 26) //has trailer @@ -174,6 +184,15 @@ public: _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff)); + if (decim > 1 and hb0 == 0 and hb1 == 0) + { + UHD_MSG(warning) << boost::format( + "The requested decimation is odd; the user should expect CIC rolloff.\n" + "Select an even decimation to ensure that a halfband filter is enabled.\n" + "decimation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" + ) % decim_rate % (_tick_rate/1e6) % (rate/1e6); + } + // Calculate CIC decimation (i.e., without halfband decimators) // Calculate closest multiplier constant to reverse gain absent scale multipliers const double rate_pow = std::pow(double(decim & 0xff), 4); diff --git a/host/lib/usrp/cores/time64_core_200.cpp b/host/lib/usrp/cores/time64_core_200.cpp index e460d1106..11b310362 100644 --- a/host/lib/usrp/cores/time64_core_200.cpp +++ b/host/lib/usrp/cores/time64_core_200.cpp @@ -56,6 +56,10 @@ public: if (_mimo_delay_cycles != 0) _sources.push_back("mimo"); } + void enable_gpsdo(void){ + _sources.push_back("gpsdo"); + } + void set_tick_rate(const double rate){ _tick_rate = rate; } @@ -100,7 +104,7 @@ public: assert_has(_sources, source, "time source"); //setup pps flags - if (source == "external"){ + if (source == "external" or source == "gpsdo"){ _iface->poke32(REG_TIME64_FLAGS, FLAG_TIME64_PPS_SMA | FLAG_TIME64_PPS_POSEDGE); } else if (source == "_external_"){ diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp index 7571573a5..315f2ba67 100644 --- a/host/lib/usrp/cores/time64_core_200.hpp +++ b/host/lib/usrp/cores/time64_core_200.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -42,6 +42,8 @@ public: const size_t mimo_delay_cycles = 0 // 0 means no-mimo ); + virtual void enable_gpsdo(void) = 0; + virtual void set_tick_rate(const double rate) = 0; virtual uhd::time_spec_t get_time_now(void) = 0; diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index f905a7551..808f13028 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -126,6 +126,15 @@ public: _iface->poke32(REG_DSP_TX_INTERP, (hb1 << 9) | (hb0 << 8) | (interp & 0xff)); + if (interp > 1 and hb0 == 0 and hb1 == 0) + { + UHD_MSG(warning) << boost::format( + "The requested interpolation is odd; the user should expect CIC rolloff.\n" + "Select an even interpolation to ensure that a halfband filter is enabled.\n" + "interpolation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" + ) % interp_rate % (_tick_rate/1e6) % (rate/1e6); + } + // Calculate CIC interpolation (i.e., without halfband interpolators) // Calculate closest multiplier constant to reverse gain absent scale multipliers const double rate_pow = std::pow(double(interp & 0xff), 3); diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index fc42a73d5..2b30dab52 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -108,11 +108,17 @@ basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set( - std::string(str(boost::format("%s - %s") - % get_rx_id().to_pp_string() - % get_subdev_name() + if(get_rx_id() == 0x0001){ + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("BasicRX (%s)") % get_subdev_name() ))); + } + else{ + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("LFRX (%s)") % get_subdev_name() + ))); + } + this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists this->get_rx_subtree()->create<double>("freq/value") .publish(&always_zero_freq); @@ -157,11 +163,17 @@ basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set( - std::string(str(boost::format("%s - %s") - % get_tx_id().to_pp_string() - % get_subdev_name() + if(get_tx_id() == 0x0000){ + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("BasicTX (%s)") % get_subdev_name() ))); + } + else{ + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("LFTX (%s)") % get_subdev_name() + ))); + } + this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists this->get_tx_subtree()->create<double>("freq/value") .publish(&always_zero_freq); diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index 95c5c5d4d..b1cee4aa7 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -202,7 +202,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("DBSRX"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&dbsrx::get_locked, this)); BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){ diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index 517b7b183..013f3178a 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -189,7 +189,7 @@ dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("DBSRX2"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&dbsrx2::get_locked, this)); BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){ diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 32aa3fe04..cf3b29ddc 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -174,7 +174,14 @@ rfx_xcvr::rfx_xcvr( //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); + if(get_rx_id() == 0x0024) this->get_rx_subtree()->create<std::string>("name").set("RFX400 RX"); + else if(get_rx_id() == 0x0025) this->get_rx_subtree()->create<std::string>("name").set("RFX900 RX"); + else if(get_rx_id() == 0x0034) this->get_rx_subtree()->create<std::string>("name").set("RFX1800 RX"); + else if(get_rx_id() == 0x0026) this->get_rx_subtree()->create<std::string>("name").set("RFX1200 RX"); + else if(get_rx_id() == 0x002c) this->get_rx_subtree()->create<std::string>("name").set("RFX2200 RX"); + else if(get_rx_id() == 0x0027) this->get_rx_subtree()->create<std::string>("name").set("RFX2400 RX"); + else this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){ @@ -203,7 +210,14 @@ rfx_xcvr::rfx_xcvr( //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); + if(get_tx_id() == 0x0028) this->get_tx_subtree()->create<std::string>("name").set("RFX400 TX"); + else if(get_tx_id() == 0x0029) this->get_tx_subtree()->create<std::string>("name").set("RFX900 TX"); + else if(get_tx_id() == 0x0035) this->get_tx_subtree()->create<std::string>("name").set("RFX1800 TX"); + else if(get_tx_id() == 0x002a) this->get_tx_subtree()->create<std::string>("name").set("RFX1200 TX"); + else if(get_tx_id() == 0x002d) this->get_tx_subtree()->create<std::string>("name").set("RFX2200 TX"); + else if(get_tx_id() == 0x002b) this->get_tx_subtree()->create<std::string>("name").set("RFX2400 TX"); + else this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists @@ -258,7 +272,7 @@ void rfx_xcvr::set_rx_ant(const std::string &ant){ //set the rx atr regs that change with antenna setting if (ant == "CAL") { - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TXRX | MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TXRX | MIXER_ENB); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX | MIXER_ENB); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, _power_up | MIXER_ENB | ANT_TXRX ); } @@ -358,7 +372,7 @@ double rfx_xcvr::set_lo_freq( * The goal here to to loop though possible R dividers, * band select clock dividers, and prescaler values. * Calculate the A and B counters for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * fvco = [P*B + A] * fref/R diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index d1cd5b373..728cb17e9 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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,7 +16,6 @@ // #include "db_sbx_common.hpp" -#include "adf4350_regs.hpp" using namespace uhd; using namespace uhd::usrp; @@ -127,7 +126,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + if(get_rx_id() == 0x054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX"); + else if(get_rx_id() == 0x065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX"); + else this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ @@ -156,7 +158,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + if(get_tx_id() == 0x055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX"); + else if(get_tx_id() == 0x067) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX"); + else this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ @@ -213,8 +218,8 @@ void sbx_xcvr::update_atr(void){ int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; - int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD; - int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD; + int rx_ld_led = _rx_lo_lock_cache ? 0 : RX_LED_LD; + int tx_ld_led = _tx_lo_lock_cache ? 0 : TX_LED_LD; int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; int tx_ant_led = _tx_ant == "TX/RX" ? 0 : TX_LED_TXRX; @@ -283,8 +288,14 @@ void sbx_xcvr::set_tx_ant(const std::string &ant){ **********************************************************************/ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { const double actual = db_actual->set_lo_freq(unit, target_freq); - if (unit == dboard_iface::UNIT_RX) _rx_lo_freq = actual; - if (unit == dboard_iface::UNIT_TX) _tx_lo_freq = actual; + if (unit == dboard_iface::UNIT_RX){ + _rx_lo_lock_cache = false; + _rx_lo_freq = actual; + } + if (unit == dboard_iface::UNIT_TX){ + _tx_lo_lock_cache = false; + _tx_lo_freq = actual; + } update_atr(); return actual; } @@ -292,6 +303,13 @@ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) { const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + + if (unit == dboard_iface::UNIT_RX) _rx_lo_lock_cache = locked; + if (unit == dboard_iface::UNIT_TX) _tx_lo_lock_cache = locked; + + //write the new lock cache setting to atr regs + update_atr(); + return sensor_value_t("LO", locked, "locked", "unlocked"); } diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 501a7f1fc..2a0e83115 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -138,6 +138,7 @@ protected: uhd::dict<std::string, double> _tx_gains, _rx_gains; double _rx_lo_freq, _tx_lo_freq; std::string _tx_ant, _rx_ant; + bool _rx_lo_lock_cache, _tx_lo_lock_cache; void set_rx_ant(const std::string &ant); void set_tx_ant(const std::string &ant); diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index 6e20d5882..2765d530c 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -75,7 +75,6 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -83,7 +82,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -91,7 +90,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -110,7 +109,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if (pfd_freq > 25e6) continue; //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); + N = int(std::floor(target_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < prescaler_to_min_int_div[prescaler]) continue; @@ -125,7 +124,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -135,12 +134,12 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -155,6 +154,9 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -163,6 +165,11 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + //reset the N and R counter + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index c8128d5f4..27fd68b05 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -78,7 +78,6 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -86,7 +85,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -94,7 +93,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -128,7 +127,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -138,12 +137,12 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -158,6 +157,9 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -166,6 +168,11 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + //reset the N and R counter + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index fd86d5b83..edee46cd5 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -186,7 +186,7 @@ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("TVRX"); this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ this->get_rx_subtree()->create<double>("gains/"+name+"/value") diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index 628221527..0bfa5229a 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,2012 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 @@ -955,7 +955,7 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("TVRX2"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&tvrx2::get_locked, this)); this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp index 58ce03d79..503e5aabf 100644 --- a/host/lib/usrp/dboard/db_wbx_common.cpp +++ b/host/lib/usrp/dboard/db_wbx_common.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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,7 +16,6 @@ // #include "db_wbx_common.hpp" -#include "adf4350_regs.hpp" #include <uhd/types/dict.hpp> #include <uhd/types/ranges.hpp> #include <uhd/types/sensors.hpp> diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index 3d633a672..4ba30255d 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -81,8 +81,10 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->access<std::string>("name").set( - this->get_rx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + std::string(str(boost::format("%s+GDB") % this->get_rx_subtree()->access<std::string>("name").get() + ))); this->get_rx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&wbx_simple::set_rx_ant, this, _1)) .set("RX2"); @@ -93,7 +95,8 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ // Register TX properties //////////////////////////////////////////////////////////////////// this->get_tx_subtree()->access<std::string>("name").set( - this->get_tx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + std::string(str(boost::format("%s+GDB") % this->get_tx_subtree()->access<std::string>("name").get() + ))); this->get_tx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&wbx_simple::set_tx_ant, this, _1)) .set(wbx_tx_antennas.at(0)); diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index ad31339e7..5f6118a91 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -78,7 +78,7 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v2"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv2 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0); @@ -87,7 +87,7 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v2"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv2 TX"); BOOST_FOREACH(const std::string &name, wbx_v2_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name)) @@ -166,6 +166,9 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -193,12 +196,13 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; @@ -209,7 +213,7 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -253,14 +257,13 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -270,6 +273,12 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -307,6 +316,11 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar } + //reset the N and R counter + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index 7ef47edd4..3e8fc8095 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -84,7 +84,7 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v3"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv3 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0); @@ -93,7 +93,7 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v3"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv3 TX"); BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name)) @@ -198,6 +198,9 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -225,12 +228,13 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; @@ -241,7 +245,7 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -285,14 +289,13 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -302,6 +305,12 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -339,6 +348,11 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } + //reset the N and R counter + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 3a85826cd..58a5e5f3e 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -85,7 +85,7 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v4"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv4 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); @@ -94,7 +94,7 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v4"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv4 TX"); BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name)) @@ -179,6 +179,9 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4351_regs_t::PRESCALER_4_5 @@ -208,12 +211,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; @@ -268,14 +272,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -285,6 +288,12 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -322,6 +331,11 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } + //reset the N and R counter + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 439e1b35e..348195a6e 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -228,7 +228,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ // Register RX properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("XCVR2450 RX"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&xcvr2450::get_locked, this)); this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") @@ -266,7 +266,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ // Register TX properties //////////////////////////////////////////////////////////////////// this->get_tx_subtree()->create<std::string>("name") - .set(get_tx_id().to_pp_string()); + .set("XCVR2450 TX"); this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&xcvr2450::get_locked, this)); BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){ diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp index 6afc7bc48..532b2dc9e 100644 --- a/host/lib/usrp/e100/dboard_iface.cpp +++ b/host/lib/usrp/e100/dboard_iface.cpp @@ -45,7 +45,7 @@ public: _spi_iface = spi_iface; _clock = clock; _codec = codec; - _gpio = gpio_core_200::make(_wb_iface, E100_REG_SR_ADDR(UE_SR_GPIO), E100_REG_RB_GPIO); + _gpio = gpio_core_200::make(_wb_iface, TOREG(SR_GPIO), REG_RB_GPIO); //init the clock rate shadows this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); diff --git a/host/lib/usrp/e100/e100_ctrl.cpp b/host/lib/usrp/e100/e100_ctrl.cpp index eb529c9c1..5a9b93633 100644 --- a/host/lib/usrp/e100/e100_ctrl.cpp +++ b/host/lib/usrp/e100/e100_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -23,13 +23,16 @@ #include <sys/ioctl.h> //ioctl #include <fcntl.h> //open, close #include <linux/usrp_e.h> //ioctl structures and constants +#include <poll.h> //poll #include <boost/thread/thread.hpp> //sleep #include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> #include <fstream> using namespace uhd; +using namespace uhd::transport; /*********************************************************************** * Sysfs GPIO wrapper class @@ -71,6 +74,11 @@ private: }; /*********************************************************************** + * Protection for dual GPIO access - sometimes MISO, sometimes have resp + **********************************************************************/ +static boost::mutex gpio_irq_resp_mutex; + +/*********************************************************************** * Aux spi implementation **********************************************************************/ class aux_spi_iface_impl : public spi_iface{ @@ -87,6 +95,8 @@ public: size_t num_bits, bool readback ){ + boost::mutex::scoped_lock lock(gpio_irq_resp_mutex); + boost::uint32_t rb_bits = 0; this->spi_sen_gpio(0); @@ -244,6 +254,57 @@ uhd::uart_iface::sptr e100_ctrl::make_gps_uart_iface(const std::string &node){ } /*********************************************************************** + * Simple managed buffers + **********************************************************************/ +struct e100_simpl_mrb : managed_recv_buffer +{ + usrp_e_ctl32 data; + e100_ctrl *ctrl; + + void release(void) + { + //NOP + } + + sptr get_new(void) + { + const size_t max_words32 = 8; //.LAST_ADDR(10'h00f)) resp_fifo_to_gpmc + + //load the data struct + data.offset = 0; + data.count = max_words32; + + //call the ioctl + ctrl->ioctl(USRP_E_READ_CTL32, &data); + + if (data.buf[0] == 0 or ~data.buf[0] == 0) return sptr(); //bad VRT hdr, treat like timeout + + return make(this, data.buf, sizeof(data.buf)); + } +}; + +struct e100_simpl_msb : managed_send_buffer +{ + usrp_e_ctl32 data; + e100_ctrl *ctrl; + + void release(void) + { + //load the data struct + data.offset = 0; + data.count = size()/4+1/*1 for header offset*/; + + //call the ioctl + ctrl->ioctl(USRP_E_WRITE_CTL32, &data); + } + + sptr get_new(void) + { + return make(this, data.buf+1, sizeof(data.buf)-4); + } +}; + +/*********************************************************************** * USRP-E100 control implementation **********************************************************************/ class e100_ctrl_impl : public e100_ctrl{ @@ -273,11 +334,24 @@ public: ) % USRP_E_COMPAT_NUMBER % module_compat_num)); } - //perform a global reset after opening - this->poke32(E100_REG_GLOBAL_RESET, 0); + //hit the magic arst condition + //async_reset <= ~EM_NCS6 && ~EM_NWE && (EM_A[9:2] == 8'hff) && EM_D[0]; + usrp_e_ctl16 datax; + datax.offset = 0x3fc; + datax.count = 2; + datax.buf[0] = 1; + datax.buf[1] = 0; + this->ioctl(USRP_E_WRITE_CTL16, &datax); + + std::ofstream edge_file("/sys/class/gpio/gpio147/edge"); + edge_file << "rising" << std::endl << std::flush; + edge_file.close(); + _irq_fd = ::open("/sys/class/gpio/gpio147/value", O_RDONLY); + if (_irq_fd < 0) UHD_MSG(error) << "Unable to open GPIO for IRQ\n"; } ~e100_ctrl_impl(void){ + ::close(_irq_fd); ::close(_node_fd); } @@ -293,58 +367,76 @@ public: )); } } + /******************************************************************* - * Peek and Poke + * The managed buffer interface ******************************************************************/ - void poke32(wb_addr_type addr, boost::uint32_t value){ - //load the data struct - usrp_e_ctl32 data; - data.offset = addr; - data.count = 1; - data.buf[0] = value; - - //call the ioctl - this->ioctl(USRP_E_WRITE_CTL32, &data); + UHD_INLINE bool resp_read(void) + { + //thread stuff ensures that this GPIO isnt shared + boost::mutex::scoped_lock lock(gpio_irq_resp_mutex); + + //perform a read of the GPIO IRQ state + char ch; + ::read(_irq_fd, &ch, sizeof(ch)); + ::lseek(_irq_fd, SEEK_SET, 0); + return ch == '1'; } - void poke16(wb_addr_type addr, boost::uint16_t value){ - //load the data struct - usrp_e_ctl16 data; - data.offset = addr; - data.count = 1; - data.buf[0] = value; + UHD_INLINE bool resp_wait(const double timeout) + { + //perform a check, if it fails, poll + if (this->resp_read()) return true; - //call the ioctl - this->ioctl(USRP_E_WRITE_CTL16, &data); + //poll IRQ GPIO for some action + pollfd pfd; + pfd.fd = _irq_fd; + pfd.events = POLLPRI | POLLERR; + ::poll(&pfd, 1, long(timeout*1000)/*ms*/); + + //perform a GPIO read again for result + return this->resp_read(); } - boost::uint32_t peek32(wb_addr_type addr){ - //load the data struct - usrp_e_ctl32 data; - data.offset = addr; - data.count = 1; + managed_recv_buffer::sptr get_recv_buff(double timeout) + { + if (not this->resp_wait(timeout)) + { + return managed_recv_buffer::sptr(); + } - //call the ioctl - this->ioctl(USRP_E_READ_CTL32, &data); + _mrb.ctrl = this; + return _mrb.get_new(); + } - return data.buf[0]; + managed_send_buffer::sptr get_send_buff(double) + { + _msb.ctrl = this; + return _msb.get_new(); } - boost::uint16_t peek16(wb_addr_type addr){ - //load the data struct - usrp_e_ctl16 data; - data.offset = addr; - data.count = 1; + size_t get_num_recv_frames(void) const{ + return 1; + } - //call the ioctl - this->ioctl(USRP_E_READ_CTL16, &data); + size_t get_recv_frame_size(void) const{ + return sizeof(_mrb.data.buf); + } + + size_t get_num_send_frames(void) const{ + return 1; + } - return data.buf[0]; + size_t get_send_frame_size(void) const{ + return sizeof(_msb.data.buf); } private: int _node_fd; + int _irq_fd; boost::mutex _ioctl_mutex; + e100_simpl_mrb _mrb; + e100_simpl_msb _msb; }; /*********************************************************************** diff --git a/host/lib/usrp/e100/e100_ctrl.hpp b/host/lib/usrp/e100/e100_ctrl.hpp index fd66791d4..72dce134e 100644 --- a/host/lib/usrp/e100/e100_ctrl.hpp +++ b/host/lib/usrp/e100/e100_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2012 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 @@ -18,12 +18,11 @@ #ifndef INCLUDED_B100_CTRL_HPP #define INCLUDED_B100_CTRL_HPP -#include "wb_iface.hpp" +#include <uhd/transport/zero_copy.hpp> #include <uhd/types/serial.hpp> #include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -class e100_ctrl : boost::noncopyable, public wb_iface{ +class e100_ctrl : public uhd::transport::zero_copy_if{ public: typedef boost::shared_ptr<e100_ctrl> sptr; @@ -33,7 +32,7 @@ public: //! Make an i2c iface for the i2c device node static uhd::i2c_iface::sptr make_dev_i2c_iface(const std::string &node); - //! Make an i2c iface for the i2c device node + //! Make a spi iface for the spi gpio static uhd::spi_iface::sptr make_aux_spi_iface(void); //! Make a uart iface for the uart device node diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index d610c0b12..a0fa6c47e 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -28,6 +28,7 @@ #include <boost/functional/hash.hpp> #include <boost/assign/list_of.hpp> #include <fstream> +#include <ctime> using namespace uhd; using namespace uhd::usrp; @@ -64,7 +65,7 @@ static device_addrs_t e100_find(const device_addr_t &hint){ new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string(); try{ i2c_iface::sptr i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*i2c_iface, mboard_eeprom_t::MAP_E100); + const mboard_eeprom_t mb_eeprom(*i2c_iface, E100_EEPROM_MAP_KEY); new_addr["name"] = mb_eeprom["name"]; new_addr["serial"] = mb_eeprom["serial"]; } @@ -86,15 +87,6 @@ static device_addrs_t e100_find(const device_addr_t &hint){ /*********************************************************************** * Make **********************************************************************/ -static size_t hash_fpga_file(const std::string &file_path){ - size_t hash = 0; - std::ifstream file(file_path.c_str()); - if (not file.good()) throw uhd::io_error("cannot open fpga file for read: " + file_path); - while (file.good()) boost::hash_combine(hash, file.get()); - file.close(); - return hash; -} - static device::sptr e100_make(const device_addr_t &device_addr){ return device::sptr(new e100_impl(device_addr)); } @@ -114,13 +106,9 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree = property_tree::make(); - //setup the main interface into fpga - const std::string node = device_addr["node"]; - _fpga_ctrl = e100_ctrl::make(node); - //read the eeprom so we can determine the hardware _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100); + const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); @@ -133,20 +121,19 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //extract the fpga path and compute hash const std::string default_fpga_file_name = model_to_fpga_file_name[model]; - const std::string e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); - const boost::uint32_t file_hash = boost::uint32_t(hash_fpga_file(e100_fpga_image)); - - //When the hash does not match: - // - close the device node - // - load the fpga bin file - // - re-open the device node - if (_fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash){ - _fpga_ctrl.reset(); - e100_load_fpga(e100_fpga_image); - _fpga_ctrl = e100_ctrl::make(node); + std::string e100_fpga_image; + try{ + e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); } + catch(...){ + UHD_MSG(error) << boost::format("Could not find FPGA image. %s\n") % print_images_error(); + throw; + } + e100_load_fpga(e100_fpga_image); - //setup clock control here to ensure that the FPGA has a good clock before we continue + //////////////////////////////////////////////////////////////////// + // Setup the FPGA clock over AUX SPI + //////////////////////////////////////////////////////////////////// bool dboard_clocks_diff = true; if (mb_eeprom.get("revision", "0") == "3") dboard_clocks_diff = false; else if (mb_eeprom.get("revision", "0") == "4") dboard_clocks_diff = true; @@ -158,12 +145,32 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _aux_spi_iface = e100_ctrl::make_aux_spi_iface(); _clock_ctrl = e100_clock_ctrl::make(_aux_spi_iface, master_clock_rate, dboard_clocks_diff); + //////////////////////////////////////////////////////////////////// + // setup the main interface into fpga + // - do this after aux spi, because we share gpio147 + //////////////////////////////////////////////////////////////////// + const std::string node = device_addr["node"]; + _fpga_ctrl = e100_ctrl::make(node); + + //////////////////////////////////////////////////////////////////// + // Initialize FPGA control communication + //////////////////////////////////////////////////////////////////// + fifo_ctrl_excelsior_config fifo_ctrl_config; + fifo_ctrl_config.async_sid_base = E100_TX_ASYNC_SID; + fifo_ctrl_config.num_async_chan = 1; + fifo_ctrl_config.ctrl_sid_base = E100_CTRL_MSG_SID; + fifo_ctrl_config.spi_base = TOREG(SR_SPI); + fifo_ctrl_config.spi_rb = REG_RB_SPI; + _fifo_ctrl = fifo_ctrl_excelsior::make(_fpga_ctrl, fifo_ctrl_config); + //Perform wishbone readback tests, these tests also write the hash bool test_fail = false; - UHD_MSG(status) << "Performing wishbone readback test... " << std::flush; + UHD_MSG(status) << "Performing control readback test... " << std::flush; + size_t hash = time(NULL); for (size_t i = 0; i < 100; i++){ - _fpga_ctrl->poke32(E100_REG_SR_MISC_TEST32, file_hash); - test_fail = _fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash; + boost::hash_combine(hash, i); + _fifo_ctrl->poke32(TOREG(SR_MISC+0), boost::uint32_t(hash)); + test_fail = _fifo_ctrl->peek32(REG_RB_CONFIG0) != boost::uint32_t(hash); if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; @@ -180,8 +187,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Create controller objects //////////////////////////////////////////////////////////////////// - _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, E100_REG_SLAVE(3)); - _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, E100_REG_SLAVE(2)); + _fpga_i2c_ctrl = i2c_core_200::make(_fifo_ctrl, TOREG(SR_I2C), REG_RB_I2C); _data_transport = e100_make_mmap_zero_copy(_fpga_ctrl); //////////////////////////////////////////////////////////////////// @@ -189,7 +195,8 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("E-Series Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set(str(boost::format("%s (euewanee)") % model)); + _tree->create<std::string>(mb_path / "name").set(model); + _tree->create<std::string>(mb_path / "codename").set("Euwanee"); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -204,12 +211,17 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //^^^ clock created up top, just reg props here... ^^^ _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&e100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) .subscribe(boost::bind(&e100_impl::update_tick_rate, this, _1)); + //subscribe the command time while we are at it + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); + //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// - _codec_ctrl = e100_codec_ctrl::make(_fpga_spi_ctrl); + _codec_ctrl = e100_codec_ctrl::make(_fifo_ctrl/*spi*/); const fs_path rx_codec_path = mb_path / "rx_codecs/A"; const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); @@ -231,24 +243,37 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Create the GPSDO control //////////////////////////////////////////////////////////////////// - try{ - _gps = gps_ctrl::make(e100_ctrl::make_gps_uart_iface(E100_UART_DEV_NODE)); - } - catch(std::exception &e){ - UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; - } - if (_gps.get() != NULL and _gps->gps_detected()){ - BOOST_FOREACH(const std::string &name, _gps->get_sensors()){ - _tree->create<sensor_value_t>(mb_path / "sensors" / name) - .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + static const fs::path GPSDO_VOLATILE_PATH("/media/ram/e100_internal_gpsdo.cache"); + if (not fs::exists(GPSDO_VOLATILE_PATH)) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try{ + _gps = gps_ctrl::make(e100_ctrl::make_gps_uart_iface(E100_UART_DEV_NODE)); + } + catch(std::exception &e){ + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_gps and _gps->gps_detected()) + { + UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + } + } + else + { + UHD_MSG(status) << "not found" << std::endl; + std::ofstream(GPSDO_VOLATILE_PATH.string().c_str(), std::ofstream::binary) << "42" << std::endl; } } //////////////////////////////////////////////////////////////////// // create frontend control objects //////////////////////////////////////////////////////////////////// - _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_FRONT)); - _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_FRONT)); + _rx_fe = rx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_RX_FE)); + _tx_fe = tx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_TX_FE)); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") .subscribe(boost::bind(&e100_impl::update_rx_subdev_spec, this, _1)); @@ -277,13 +302,17 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_DSP0), E100_REG_SR_ADDR(UE_SR_RX_CTRL0), E100_RX_SID_BASE + 0 - )); - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_DSP1), E100_REG_SR_ADDR(UE_SR_RX_CTRL1), E100_RX_SID_BASE + 1 - )); - for (size_t dspno = 0; dspno < _rx_dsps.size(); dspno++){ + const size_t num_rx_dsps = _fifo_ctrl->peek32(REG_RB_NUM_RX_DSP); + for (size_t dspno = 0; dspno < num_rx_dsps; dspno++) + { + const size_t sr_off = dspno*32; + _rx_dsps.push_back(rx_dsp_core_200::make( + _fifo_ctrl, + TOREG(SR_RX_DSP0+sr_off), + TOREG(SR_RX_CTRL0+sr_off), + E100_RX_SID_BASE + dspno + )); + _rx_dsps[dspno]->set_link_rate(E100_RX_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); @@ -306,7 +335,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////////// _tx_dsp = tx_dsp_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_DSP), E100_REG_SR_ADDR(UE_SR_TX_CTRL), E100_TX_ASYNC_SID + _fifo_ctrl, TOREG(SR_TX_DSP), TOREG(SR_TX_CTRL), E100_TX_ASYNC_SID ); _tx_dsp->set_link_rate(E100_TX_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -326,12 +355,12 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ // create time control objects //////////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_hi_now = E100_REG_RB_TIME_NOW_HI; - time64_rb_bases.rb_lo_now = E100_REG_RB_TIME_NOW_LO; - time64_rb_bases.rb_hi_pps = E100_REG_RB_TIME_PPS_HI; - time64_rb_bases.rb_lo_pps = E100_REG_RB_TIME_PPS_LO; + time64_rb_bases.rb_hi_now = REG_RB_TIME_NOW_HI; + time64_rb_bases.rb_lo_now = REG_RB_TIME_NOW_LO; + time64_rb_bases.rb_hi_pps = REG_RB_TIME_PPS_HI; + time64_rb_bases.rb_lo_pps = REG_RB_TIME_PPS_LO; _time64 = time64_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TIME64), time64_rb_bases + _fifo_ctrl, TOREG(SR_TIME64), time64_rb_bases ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1)); @@ -349,13 +378,14 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") .subscribe(boost::bind(&e100_impl::update_clock_source, this, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto"); + std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto"); + if (_gps and _gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _user = user_settings_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_USER_REGS)); + _user = user_settings_core_200::make(_fifo_ctrl, TOREG(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); @@ -369,6 +399,9 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ tx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB); gdb_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB ^ 5); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(rx_fe_path / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom") .set(rx_db_eeprom) @@ -381,7 +414,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "gdb", _1)); //create a new dboard interface and manager - _dboard_iface = make_e100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl); + _dboard_iface = make_e100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface); _dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -401,7 +434,11 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ } //initialize io handling - this->io_init(); + _recv_demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE); + + //allocate streamer weak ptrs containers + _rx_streamers.resize(_rx_dsps.size()); + _tx_streamers.resize(1/*known to be 1 dsp*/); //////////////////////////////////////////////////////////////////// // do some post-init tasks @@ -425,10 +462,11 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree->access<std::string>(mb_path / "time_source/value").set("none"); //GPS installed: use external ref, time, and init time spec - if (_gps.get() != NULL and _gps->gps_detected()){ + if (_gps and _gps->gps_detected()){ + _time64->enable_gpsdo(); UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(mb_path / "time_source/value").set("external"); - _tree->access<std::string>(mb_path / "clock_source/value").set("external"); + _tree->access<std::string>(mb_path / "time_source/value").set("gpsdo"); + _tree->access<std::string>(mb_path / "clock_source/value").set("gpsdo"); UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; _time64->set_time_next_pps(time_spec_t(time_t(_gps->get_sensor("gps_time").to_int()+1))); } @@ -448,7 +486,7 @@ double e100_impl::update_rx_codec_gain(const double gain){ } void e100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100); + mb_eeprom.commit(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); } void e100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -458,9 +496,23 @@ void e100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_e } void e100_impl::update_clock_source(const std::string &source){ + + if (source == "pps_sync"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 1); + return; + } + if (source == "_pps_sync_"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 3); + return; + } + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 0); + if (source == "auto") _clock_ctrl->use_auto_ref(); else if (source == "internal") _clock_ctrl->use_internal_ref(); else if (source == "external") _clock_ctrl->use_external_ref(); + else if (source == "gpsdo") _clock_ctrl->use_external_ref(); else throw uhd::runtime_error("unhandled clock configuration reference source: " + source); } @@ -470,7 +522,7 @@ sensor_value_t e100_impl::get_ref_locked(void){ } void e100_impl::check_fpga_compat(void){ - const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(E100_REG_RB_COMPAT); + const boost::uint32_t fpga_compat_num = _fifo_ctrl->peek32(REG_RB_COMPAT); boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; if (fpga_major == 0){ //old version scheme fpga_major = fpga_minor; diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 1d36cb2ac..6f64d4b80 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -18,17 +18,17 @@ #include "e100_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" -#include "spi_core_100.hpp" -#include "i2c_core_100.hpp" +#include "i2c_core_200.hpp" #include "rx_frontend_core_200.hpp" #include "tx_frontend_core_200.hpp" #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "fifo_ctrl_excelsior.hpp" #include "user_settings_core_200.hpp" +#include "recv_packet_demuxer.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> -#include <uhd/utils/pimpl.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/usrp/mboard_eeprom.hpp> @@ -49,10 +49,12 @@ static const double E100_RX_LINK_RATE_BPS = 166e6/3/2*2; static const double E100_TX_LINK_RATE_BPS = 166e6/3/1*2; static const std::string E100_I2C_DEV_NODE = "/dev/i2c-3"; static const std::string E100_UART_DEV_NODE = "/dev/ttyO0"; -static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x09; -static const boost::uint32_t E100_RX_SID_BASE = 2; -static const boost::uint32_t E100_TX_ASYNC_SID = 1; +static const boost::uint16_t E100_FPGA_COMPAT_NUM = 11; +static const boost::uint32_t E100_RX_SID_BASE = 30; +static const boost::uint32_t E100_TX_ASYNC_SID = 10; +static const boost::uint32_t E100_CTRL_MSG_SID = 20; static const double E100_DEFAULT_CLOCK_RATE = 64e6; +static const std::string E100_EEPROM_MAP_KEY = "E100"; //! load an fpga image from a bin file into the usrp-e fpga extern void e100_load_fpga(const std::string &bin_file); @@ -86,8 +88,8 @@ private: uhd::property_tree::sptr _tree; //controllers - spi_core_100::sptr _fpga_spi_ctrl; - i2c_core_100::sptr _fpga_i2c_ctrl; + fifo_ctrl_excelsior::sptr _fifo_ctrl; + i2c_core_200::sptr _fpga_i2c_ctrl; rx_frontend_core_200::sptr _rx_fe; tx_frontend_core_200::sptr _tx_fe; std::vector<rx_dsp_core_200::sptr> _rx_dsps; @@ -103,15 +105,12 @@ private: //transports uhd::transport::zero_copy_if::sptr _data_transport; + uhd::usrp::recv_packet_demuxer::sptr _recv_demuxer; //dboard stuff uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //handle io stuff - UHD_PIMPL_DECL(io_impl) _io_impl; - void io_init(void); - //device properties interface uhd::property_tree::sptr get_tree(void) const{ return _tree; diff --git a/host/lib/usrp/e100/e100_mmap_zero_copy.cpp b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp index cdb7094f4..58beeb424 100644 --- a/host/lib/usrp/e100/e100_mmap_zero_copy.cpp +++ b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -19,6 +19,7 @@ #include <uhd/transport/zero_copy.hpp> #include <uhd/utils/log.hpp> #include <uhd/exception.hpp> +#include <boost/make_shared.hpp> #include <linux/usrp_e.h> #include <sys/mman.h> //mmap #include <unistd.h> //getpagesize @@ -41,23 +42,19 @@ public: _mem(mem), _info(info) { /* NOP */ } void release(void){ - if (_info->flags != RB_USER_PROCESS) return; if (fp_verbose) UHD_LOGV(always) << "recv buff: release" << std::endl; _info->flags = RB_KERNEL; //release the frame } - bool ready(void){return _info->flags & RB_USER;} + UHD_INLINE bool ready(void){return _info->flags & RB_USER;} - sptr get_new(void){ - if (fp_verbose) UHD_LOGV(always) << " make_recv_buff: " << get_size() << std::endl; + UHD_INLINE sptr get_new(void){ + if (fp_verbose) UHD_LOGV(always) << " make_recv_buff: " << _info->len << std::endl; _info->flags = RB_USER_PROCESS; //claim the frame - return make_managed_buffer(this); + return make(this, _mem, _info->len); } private: - const void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _info->len;} - void *_mem; ring_buffer_info *_info; }; @@ -71,28 +68,24 @@ public: e100_mmap_zero_copy_msb(void *mem, ring_buffer_info *info, size_t len, int fd): _mem(mem), _info(info), _len(len), _fd(fd) { /* NOP */ } - void commit(size_t len){ - if (_info->flags != RB_USER_PROCESS) return; - if (fp_verbose) UHD_LOGV(always) << "send buff: commit " << len << std::endl; - _info->len = len; + void release(void){ + if (fp_verbose) UHD_LOGV(always) << "send buff: commit " << size() << std::endl; + _info->len = size(); _info->flags = RB_USER; //release the frame if (::write(_fd, NULL, 0) < 0){ //notifies the kernel UHD_LOGV(rarely) << UHD_THROW_SITE_INFO("write error") << std::endl; } } - bool ready(void){return _info->flags & RB_KERNEL;} + UHD_INLINE bool ready(void){return _info->flags & RB_KERNEL;} - sptr get_new(void){ - if (fp_verbose) UHD_LOGV(always) << " make_send_buff: " << get_size() << std::endl; + UHD_INLINE sptr get_new(void){ + if (fp_verbose) UHD_LOGV(always) << " make_send_buff: " << _len << std::endl; _info->flags = RB_USER_PROCESS; //claim the frame - return make_managed_buffer(this); + return make(this, _mem, _len); } private: - void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} - void *_mem; ring_buffer_info *_info; size_t _len; @@ -162,14 +155,14 @@ public: //initialize the managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _mrb_pool.push_back(e100_mmap_zero_copy_mrb( + _mrb_pool.push_back(boost::make_shared<e100_mmap_zero_copy_mrb>( recv_buff + get_recv_frame_size()*i, (*recv_info) + i )); } //initialize the managed send buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _msb_pool.push_back(e100_mmap_zero_copy_msb( + _msb_pool.push_back(boost::make_shared<e100_mmap_zero_copy_msb>( send_buff + get_send_frame_size()*i, (*send_info) + i, get_send_frame_size(), _fd )); @@ -183,7 +176,7 @@ public: managed_recv_buffer::sptr get_recv_buff(double timeout){ if (fp_verbose) UHD_LOGV(always) << "get_recv_buff: " << _recv_index << std::endl; - e100_mmap_zero_copy_mrb &mrb = _mrb_pool[_recv_index]; + e100_mmap_zero_copy_mrb &mrb = *_mrb_pool[_recv_index]; //poll/wait for a ready frame if (not mrb.ready()){ @@ -215,7 +208,7 @@ public: managed_send_buffer::sptr get_send_buff(double timeout){ if (fp_verbose) UHD_LOGV(always) << "get_send_buff: " << _send_index << std::endl; - e100_mmap_zero_copy_msb &msb = _msb_pool[_send_index]; + e100_mmap_zero_copy_msb &msb = *_msb_pool[_send_index]; //poll/wait for a ready frame if (not msb.ready()){ @@ -254,8 +247,8 @@ private: size_t _frame_size, _map_size; //re-usable managed buffers - std::vector<e100_mmap_zero_copy_mrb> _mrb_pool; - std::vector<e100_mmap_zero_copy_msb> _msb_pool; + std::vector<boost::shared_ptr<e100_mmap_zero_copy_mrb> > _mrb_pool; + std::vector<boost::shared_ptr<e100_mmap_zero_copy_msb> > _msb_pool; //indexes into sub-sections of mapped memory size_t _recv_index, _send_index; diff --git a/host/lib/usrp/e100/e100_regs.hpp b/host/lib/usrp/e100/e100_regs.hpp index 75be2cfbe..654163dce 100644 --- a/host/lib/usrp/e100/e100_regs.hpp +++ b/host/lib/usrp/e100/e100_regs.hpp @@ -15,55 +15,45 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -//////////////////////////////////////////////////////////////// -// -// Memory map for embedded wishbone bus -// -//////////////////////////////////////////////////////////////// - -// All addresses are byte addresses. All accesses are word (16-bit) accesses. -// This means that address bit 0 is usually 0. -// There are 11 bits of address for the control. - #ifndef INCLUDED_E100_REGS_HPP #define INCLUDED_E100_REGS_HPP -///////////////////////////////////////////////////// -// Slave pointers +#include <boost/cstdint.hpp> -#define E100_REG_SLAVE(n) ((n)<<7) +#define TOREG(x) ((x)*4) -///////////////////////////////////////////////////// -// Slave 0 -- Misc Regs +#define localparam static const int -#define E100_REG_MISC_BASE E100_REG_SLAVE(0) +localparam SR_MISC = 0; // 5 +localparam SR_USER_REGS = 5; // 2 -#define E100_REG_MISC_LED E100_REG_MISC_BASE + 0 -#define E100_REG_MISC_SW E100_REG_MISC_BASE + 2 -#define E100_REG_MISC_CGEN_CTRL E100_REG_MISC_BASE + 4 -#define E100_REG_MISC_CGEN_ST E100_REG_MISC_BASE + 6 -#define E100_REG_MISC_TEST E100_REG_MISC_BASE + 8 -#define E100_REG_MISC_RX_LEN E100_REG_MISC_BASE + 10 -#define E100_REG_MISC_TX_LEN E100_REG_MISC_BASE + 12 -#define E100_REG_MISC_XFER_RATE E100_REG_MISC_BASE + 14 +localparam SR_TX_CTRL = 32; // 6 +localparam SR_TX_DSP = 40; // 5 +localparam SR_TX_FE = 48; // 5 -///////////////////////////////////////////////////// -// Slave 1 -- UART -// CLKDIV is 16 bits, others are only 8 +localparam SR_RX_CTRL0 = 96; // 9 +localparam SR_RX_DSP0 = 106; // 7 +localparam SR_RX_FE = 114; // 5 -#define E100_REG_UART_BASE E100_REG_SLAVE(1) +localparam SR_RX_CTRL1 = 128; // 9 +localparam SR_RX_DSP1 = 138; // 7 -#define E100_REG_UART_CLKDIV E100_REG_UART_BASE + 0 -#define E100_REG_UART_TXLEVEL E100_REG_UART_BASE + 2 -#define E100_REG_UART_RXLEVEL E100_REG_UART_BASE + 4 -#define E100_REG_UART_TXCHAR E100_REG_UART_BASE + 6 -#define E100_REG_UART_RXCHAR E100_REG_UART_BASE + 8 +localparam SR_TIME64 = 192; // 6 +localparam SR_SPI = 208; // 3 +localparam SR_I2C = 216; // 1 +localparam SR_GPIO = 224; // 5 -///////////////////////////////////////////////////// -// Slave 2 -- SPI Core -//these are 32-bit registers mapped onto the 16-bit Wishbone bus. -//Using peek32/poke32 should allow transparent use of these registers. -#define E100_REG_SPI_BASE E100_REG_SLAVE(2) +#define REG_RB_TIME_NOW_HI TOREG(10) +#define REG_RB_TIME_NOW_LO TOREG(11) +#define REG_RB_TIME_PPS_HI TOREG(14) +#define REG_RB_TIME_PPS_LO TOREG(15) +#define REG_RB_SPI TOREG(0) +#define REG_RB_COMPAT TOREG(1) +#define REG_RB_GPIO TOREG(3) +#define REG_RB_I2C TOREG(2) +#define REG_RB_CONFIG0 TOREG(4) +#define REG_RB_CONFIG1 TOREG(5) +#define REG_RB_NUM_RX_DSP TOREG(6) //spi slave constants #define UE_SPI_SS_AD9522 (1 << 3) @@ -71,67 +61,5 @@ #define UE_SPI_SS_TX_DB (1 << 1) #define UE_SPI_SS_RX_DB (1 << 0) -//////////////////////////////////////////////// -// Slave 3 -- I2C Core - -#define E100_REG_I2C_BASE E100_REG_SLAVE(3) - -//////////////////////////////////////////////// -// Slave 5 -- Error messages buffer - -#define E100_REG_ERR_BUFF E100_REG_SLAVE(5) - -/////////////////////////////////////////////////// -// Slave 7 -- Readback Mux 32 - -#define E100_REG_RB_MUX_32_BASE E100_REG_SLAVE(7) - -#define E100_REG_RB_TIME_NOW_HI E100_REG_RB_MUX_32_BASE + 0 -#define E100_REG_RB_TIME_NOW_LO E100_REG_RB_MUX_32_BASE + 4 -#define E100_REG_RB_TIME_PPS_HI E100_REG_RB_MUX_32_BASE + 8 -#define E100_REG_RB_TIME_PPS_LO E100_REG_RB_MUX_32_BASE + 12 -#define E100_REG_RB_MISC_TEST32 E100_REG_RB_MUX_32_BASE + 16 -#define E100_REG_RB_ERR_STATUS E100_REG_RB_MUX_32_BASE + 20 -#define E100_REG_RB_COMPAT E100_REG_RB_MUX_32_BASE + 24 -#define E100_REG_RB_GPIO E100_REG_RB_MUX_32_BASE + 28 - -//////////////////////////////////////////////////// -// 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 - -// 64 total regs in address space -#define UE_SR_RX_CTRL0 0 // 9 regs (+0 to +8) -#define UE_SR_RX_DSP0 10 // 4 regs (+0 to +3) -#define UE_SR_RX_CTRL1 16 // 9 regs (+0 to +8) -#define UE_SR_RX_DSP1 26 // 4 regs (+0 to +3) -#define UE_SR_ERR_CTRL 30 // 1 reg -#define UE_SR_TX_CTRL 32 // 4 regs (+0 to +3) -#define UE_SR_TX_DSP 38 // 3 regs (+0 to +2) - -#define UE_SR_TIME64 42 // 6 regs (+0 to +5) -#define UE_SR_RX_FRONT 48 // 5 regs (+0 to +4) -#define UE_SR_TX_FRONT 54 // 5 regs (+0 to +4) - -#define UE_SR_REG_TEST32 60 // 1 reg -#define UE_SR_CLEAR_FIFO 61 // 1 reg -#define UE_SR_GLOBAL_RESET 63 // 1 reg -#define UE_SR_USER_REGS 64 // 2 regs - -#define UE_SR_GPIO 128 - -#define E100_REG_SR_ADDR(n) (E100_REG_SLAVE(8) + (4*(n))) - -#define E100_REG_SR_MISC_TEST32 E100_REG_SR_ADDR(UE_SR_REG_TEST32) -#define E100_REG_SR_ERR_CTRL E100_REG_SR_ADDR(UE_SR_ERR_CTRL) - -///////////////////////////////////////////////// -// Magic reset regs -//////////////////////////////////////////////// -#define E100_REG_CLEAR_FIFO E100_REG_SR_ADDR(UE_SR_CLEAR_FIFO) -#define E100_REG_GLOBAL_RESET E100_REG_SR_ADDR(UE_SR_GLOBAL_RESET) - -#endif +#endif /*INCLUDED_E100_REGS_HPP*/ diff --git a/host/lib/usrp/e100/include/linux/usrp_e.h b/host/lib/usrp/e100/include/linux/usrp_e.h index 37e9ee31a..f0512a40c 100644 --- a/host/lib/usrp/e100/include/linux/usrp_e.h +++ b/host/lib/usrp/e100/include/linux/usrp_e.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2010 Ettus Research, LLC + * Copyright (C) 2010-2012 Ettus Research, LLC * * Written by Philip Balister <philip@opensdr.com> * @@ -36,7 +36,7 @@ struct usrp_e_ctl32 { #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 3 +#define USRP_E_COMPAT_NUMBER 4 /* Flag defines */ #define RB_USER (1<<0) diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp index 332fe76ae..4d521e222 100644 --- a/host/lib/usrp/e100/io_impl.cpp +++ b/host/lib/usrp/e100/io_impl.cpp @@ -15,27 +15,16 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "recv_packet_demuxer.hpp" #include "validate_subdev_spec.hpp" #include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" -#include <linux/usrp_e.h> //ioctl structures and constants #include "e100_impl.hpp" -#include "e100_regs.hpp" #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> #include <uhd/utils/tasks.hpp> -#include <uhd/utils/thread_priority.hpp> -#include <uhd/transport/bounded_buffer.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> -#include <boost/bind.hpp> -#include <boost/thread/thread.hpp> -#include <poll.h> //poll -#include <fcntl.h> //open, close -#include <sstream> -#include <fstream> #include <boost/make_shared.hpp> using namespace uhd; @@ -44,134 +33,7 @@ using namespace uhd::transport; static const size_t vrt_send_header_offset_words32 = 1; -/*********************************************************************** - * io impl details (internal to this file) - * - pirate crew of 1 - * - bounded buffer - * - thread loop - * - vrt packet handler states - **********************************************************************/ -struct e100_impl::io_impl{ - io_impl(void): - false_alarm(0), async_msg_fifo(1000/*messages deep*/) - { /* NOP */ } - - double tick_rate; //set by update tick rate method - e100_ctrl::sptr iface; //so handle irq can peek and poke - void handle_irq(void); - size_t false_alarm; - //The data transport is listed first so that it is deconstructed last, - //which is after the states and booty which may hold managed buffers. - recv_packet_demuxer::sptr demuxer; - - //a pirate's life is the life for me! - void recv_pirate_loop( - spi_iface::sptr //keep a sptr to iface which shares gpio147 - ){ - //open the GPIO and set it up for an IRQ - std::ofstream edge_file("/sys/class/gpio/gpio147/edge"); - edge_file << "rising" << std::endl << std::flush; - edge_file.close(); - int fd = ::open("/sys/class/gpio/gpio147/value", O_RDONLY); - if (fd < 0) UHD_MSG(error) << "Unable to open GPIO for IRQ\n"; - - while (not boost::this_thread::interruption_requested()){ - pollfd pfd; - pfd.fd = fd; - pfd.events = POLLPRI | POLLERR; - ssize_t ret = ::poll(&pfd, 1, 100/*ms*/); - if (ret > 0) this->handle_irq(); - } - - //cleanup before thread exit - ::close(fd); - } - bounded_buffer<async_metadata_t> async_msg_fifo; - task::sptr pirate_task; -}; - -void e100_impl::io_impl::handle_irq(void){ - //check the status of the async msg buffer - const boost::uint32_t status = iface->peek32(E100_REG_RB_ERR_STATUS); - if ((status & 0x3) == 0){ //not done or error - //This could be a false-alarm because spi readback is mixed in. - //So we just sleep for a bit rather than interrupt continuously. - if (false_alarm++ > 3) boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - return; - } - false_alarm = 0; //its a real message, reset the count... - //std::cout << boost::format("status: 0x%x") % status << std::endl; - - //load the data struct and call the ioctl - usrp_e_ctl32 data; - data.offset = E100_REG_ERR_BUFF; - data.count = status >> 16; - iface->ioctl(USRP_E_READ_CTL32, &data); - //for (size_t i = 0; i < data.count; i++){ - //data.buf[i] = iface->peek32(E100_REG_ERR_BUFF + i*sizeof(boost::uint32_t)); - //std::cout << boost::format(" buff[%u] = 0x%08x\n") % i % data.buf[i]; - //} - - //unpack the vrt header and process below... - vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = data.count; - try{vrt::if_hdr_unpack_le(data.buf, if_packet_info);} - catch(const std::exception &e){ - UHD_MSG(error) << "Error unpacking vrt header:\n" << e.what() << std::endl; - goto prepare; - } - - //handle a tx async report message - if (if_packet_info.sid == E100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ - - //fill in the async metadata - async_metadata_t metadata; - load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, data.buf, tick_rate); - - //push the message onto the queue - async_msg_fifo.push_with_pop_on_full(metadata); - - //print some fastpath messages - standard_async_msg_prints(metadata); - } - - //prepare for the next round - prepare: - iface->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear - while ((iface->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle - iface->poke32(E100_REG_SR_ERR_CTRL, 1 << 1); //start -} - -/*********************************************************************** - * Helper Functions - **********************************************************************/ -void e100_impl::io_init(void){ - - //create new io impl - _io_impl = UHD_PIMPL_MAKE(io_impl, ()); - _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE); - _io_impl->iface = _fpga_ctrl; - - //clear fifo state machines - _fpga_ctrl->poke32(E100_REG_CLEAR_FIFO, 0); - - //allocate streamer weak ptrs containers - _rx_streamers.resize(_rx_dsps.size()); - _tx_streamers.resize(1/*known to be 1 dsp*/); - - //prepare the async msg buffer for incoming messages - _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear - while ((_fpga_ctrl->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle - _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 1); //start - - //spawn a pirate, yarrr! - _io_impl->pirate_task = task::make(boost::bind( - &e100_impl::io_impl::recv_pirate_loop, _io_impl.get(), _aux_spi_iface - )); -} - void e100_impl::update_tick_rate(const double rate){ - _io_impl->tick_rate = rate; //update the tick rate on all existing streamers -> thread safe for (size_t i = 0; i < _rx_streamers.size(); i++){ @@ -254,8 +116,7 @@ void e100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ bool e100_impl::recv_async_msg( async_metadata_t &async_metadata, double timeout ){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); + return _fifo_ctrl->pop_async_msg(async_metadata, timeout); } /*********************************************************************** @@ -300,7 +161,7 @@ rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){ _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 + &recv_packet_demuxer::get_recv_buff, _recv_demuxer, dsp, _1 ), true /*flush*/); my_streamer->set_overflow_handler(chan_i, boost::bind( &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 785d30296..1f4abc27e 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -84,17 +84,20 @@ static std::string uint16_bytes_to_string(const byte_vector_t &bytes){ **********************************************************************/ static const boost::uint8_t N100_EEPROM_ADDR = 0x50; -static const uhd::dict<std::string, boost::uint8_t> USRP_N100_OFFSETS = boost::assign::map_list_of - ("hardware", 0x00) - ("mac-addr", 0x02) - ("ip-addr", 0x0C) - //leave space here for other addresses (perhaps) - ("revision", 0x12) - ("product", 0x14) - ("gpsdo", 0x17) - ("serial", 0x18) - ("name", 0x18 + SERIAL_LEN) -; +struct n100_eeprom_map{ + boost::uint16_t hardware; + boost::uint8_t mac_addr[6]; + boost::uint32_t subnet; + boost::uint32_t ip_addr; + boost::uint16_t _pad0; + boost::uint16_t revision; + boost::uint16_t product; + unsigned char _pad1; + unsigned char gpsdo; + unsigned char serial[SERIAL_LEN]; + unsigned char name[NAME_MAX_LEN]; + boost::uint32_t gateway; +}; enum n200_gpsdo_type{ N200_GPSDO_NONE = 0, @@ -105,30 +108,36 @@ enum n200_gpsdo_type{ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the hardware number mb_eeprom["hardware"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), 2) ); //extract the revision number mb_eeprom["revision"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), 2) ); //extract the product code mb_eeprom["product"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), 2) ); //extract the addresses mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], 6 + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), 6 )).to_string(); boost::asio::ip::address_v4::bytes_type ip_addr_bytes; - byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], 4), ip_addr_bytes); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), 4), ip_addr_bytes); mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), 4), ip_addr_bytes); + mb_eeprom["subnet"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), 4), ip_addr_bytes); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + //gpsdo capabilities - boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], 1).at(0); + boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), 1).at(0); switch(n200_gpsdo_type(gpsdo_byte)){ case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break; case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break; @@ -137,12 +146,12 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the serial mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], SERIAL_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), SERIAL_LEN )); //extract the name mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], NAME_MAX_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), NAME_MAX_LEN )); //Empty serial correction: use the mac address to determine serial. @@ -158,32 +167,44 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //parse the revision number if (mb_eeprom.has_key("hardware")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), string_to_uint16_bytes(mb_eeprom["hardware"]) ); //parse the revision number if (mb_eeprom.has_key("revision")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), string_to_uint16_bytes(mb_eeprom["revision"]) ); //parse the product code if (mb_eeprom.has_key("product")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), string_to_uint16_bytes(mb_eeprom["product"]) ); //store the addresses if (mb_eeprom.has_key("mac-addr")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes() ); if (mb_eeprom.has_key("ip-addr")){ byte_vector_t ip_addr_bytes(4); byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), ip_addr_bytes); + } + + if (mb_eeprom.has_key("subnet")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), ip_addr_bytes); + } + + if (mb_eeprom.has_key("gateway")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), ip_addr_bytes); } //gpsdo capabilities @@ -191,18 +212,18 @@ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ boost::uint8_t gpsdo_byte = N200_GPSDO_NONE; if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL; if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD; - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], byte_vector_t(1, gpsdo_byte)); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), byte_vector_t(1, gpsdo_byte)); } //store the serial if (mb_eeprom.has_key("serial")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) ); //store the name if (mb_eeprom.has_key("name")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) ); } @@ -426,20 +447,16 @@ mboard_eeprom_t::mboard_eeprom_t(void){ /* NOP */ } -mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, map_type map){ - switch(map){ - case MAP_N100: load_n100(*this, iface); break; - case MAP_B000: load_b000(*this, iface); break; - case MAP_B100: load_b100(*this, iface); break; - case MAP_E100: load_e100(*this, iface); break; - } +mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ + if (which == "N100") load_n100(*this, iface); + if (which == "B000") load_b000(*this, iface); + if (which == "B100") load_b100(*this, iface); + if (which == "E100") load_e100(*this, iface); } -void mboard_eeprom_t::commit(i2c_iface &iface, map_type map) const{ - switch(map){ - case MAP_N100: store_n100(*this, iface); break; - case MAP_B000: store_b000(*this, iface); break; - case MAP_B100: store_b100(*this, iface); break; - case MAP_E100: store_e100(*this, iface); break; - } +void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{ + if (which == "N100") store_n100(*this, iface); + if (which == "B000") store_b000(*this, iface); + if (which == "B100") store_b100(*this, iface); + if (which == "E100") store_e100(*this, iface); } diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 93c0eada6..0331cf93a 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -21,6 +21,10 @@ #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/gain_group.hpp> +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> @@ -125,6 +129,9 @@ static tune_result_t tune_xx_subdev_and_dsp( //------------------------------------------------------------------ double lo_offset = 0.0; if (rf_fe_subtree->access<bool>("use_lo_offset").get()){ + //If the frontend has lo_offset value and range properties, trust it for lo_offset + if (rf_fe_subtree->exists("lo_offset/value")) lo_offset = rf_fe_subtree->access<double>("lo_offset/value").get(); + //If the local oscillator will be in the passband, use an offset. //But constrain the LO offset by the width of the filter bandwidth. const double rate = dsp_subtree->access<double>("rate/value").get(); @@ -143,6 +150,14 @@ static tune_result_t tune_xx_subdev_and_dsp( break; case tune_request_t::POLICY_MANUAL: + //If the rf_fe understands lo_offset settings, infer the desired lo_offset and set it + // Side effect: In TVRX2 for example, after setting the lo_offset (if_freq) with a + // POLICY_MANUAL, there is no way for the user to automatically get back to default + // if_freq without deconstruct/reconstruct the rf_fe objects. + if (rf_fe_subtree->exists("lo_offset/value")) { + rf_fe_subtree->access<double>("lo_offset/value").set(tune_request.rf_freq - tune_request.target_freq); + } + target_rf_freq = tune_request.rf_freq; rf_fe_subtree->access<double>("freq/value").set(target_rf_freq); break; @@ -214,6 +229,44 @@ public: return _dev; } + dict<std::string, std::string> get_usrp_rx_info(size_t chan){ + mboard_chan_pair mcp = rx_chan_to_mcp(chan); + dict<std::string, std::string> usrp_info; + + mboard_eeprom_t mb_eeprom = _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get(); + dboard_eeprom_t db_eeprom = _tree->access<dboard_eeprom_t>(rx_rf_fe_root(mcp.chan).branch_path().branch_path() / "rx_eeprom").get(); + + usrp_info["mboard_id"] = _tree->access<std::string>(mb_root(mcp.mboard) / "name").get(); + usrp_info["mboard_name"] = mb_eeprom["name"]; + usrp_info["mboard_serial"] = mb_eeprom["serial"]; + usrp_info["rx_id"] = db_eeprom.id.to_pp_string(); + usrp_info["rx_subdev_name"] = _tree->access<std::string>(rx_rf_fe_root(mcp.chan) / "name").get(); + usrp_info["rx_subdev_spec"] = _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "rx_subdev_spec").get().to_string(); + usrp_info["rx_serial"] = db_eeprom.serial; + usrp_info["rx_antenna"] = _tree->access<std::string>(rx_rf_fe_root(mcp.chan) / "antenna" / "value").get(); + + return usrp_info; + } + + dict<std::string, std::string> get_usrp_tx_info(size_t chan){ + mboard_chan_pair mcp = tx_chan_to_mcp(chan); + dict<std::string, std::string> usrp_info; + + mboard_eeprom_t mb_eeprom = _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get(); + dboard_eeprom_t db_eeprom = _tree->access<dboard_eeprom_t>(tx_rf_fe_root(mcp.chan).branch_path().branch_path() / "tx_eeprom").get(); + + usrp_info["mboard_id"] = _tree->access<std::string>(mb_root(mcp.mboard) / "name").get(); + usrp_info["mboard_name"] = mb_eeprom["name"]; + usrp_info["mboard_serial"] = mb_eeprom["serial"]; + usrp_info["tx_id"] = db_eeprom.id.to_pp_string(); + usrp_info["tx_subdev_name"] = _tree->access<std::string>(tx_rf_fe_root(mcp.chan) / "name").get(); + usrp_info["tx_subdev_spec"] = _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "tx_subdev_spec").get().to_string(); + usrp_info["tx_serial"] = db_eeprom.serial; + usrp_info["tx_antenna"] = _tree->access<std::string>(tx_rf_fe_root(mcp.chan) / "antenna" / "value").get(); + + return usrp_info; + } + /******************************************************************* * Mboard methods ******************************************************************/ @@ -356,12 +409,27 @@ public: return true; } - void set_command_time(const time_spec_t &, size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void set_command_time(const time_spec_t &time_spec, size_t mboard){ + if (mboard != ALL_MBOARDS){ + if (not _tree->exists(mb_root(mboard) / "time/cmd")){ + throw uhd::not_implemented_error("timed command feature not implemented on this hardware"); + } + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + set_command_time(time_spec, m); + } } - void clear_command_time(size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void clear_command_time(size_t mboard){ + if (mboard != ALL_MBOARDS){ + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec_t(0.0)); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + clear_command_time(m); + } } void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){ @@ -522,6 +590,10 @@ public: ); } + freq_range_t get_fe_rx_freq_range(size_t chan){ + return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get(); + } + void set_rx_gain(double gain, const std::string &name, size_t chan){ return rx_gain_group(chan)->set_value(gain, name); } @@ -621,10 +693,6 @@ public: return _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get(); } - std::string get_tx_subdev_name(size_t chan){ - return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get(); - } - size_t get_tx_num_channels(void){ size_t sum = 0; for (size_t m = 0; m < get_num_mboards(); m++){ @@ -633,6 +701,10 @@ public: return sum; } + std::string get_tx_subdev_name(size_t chan){ + return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get(); + } + void set_tx_rate(double rate, size_t chan){ if (chan != ALL_CHANS){ _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").set(rate); @@ -670,6 +742,10 @@ public: ); } + freq_range_t get_fe_tx_freq_range(size_t chan){ + return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "freq" / "range").get(); + } + void set_tx_gain(double gain, const std::string &name, size_t chan){ return tx_gain_group(chan)->set_value(gain, name); } @@ -832,7 +908,7 @@ private: const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); gain_group::sptr gg = gain_group::make(); BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains")){ - gg->register_fcns("ADC-"+name, make_gain_fcns_from_subtree(_tree->subtree(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains" / name)), 1 /* high prio */); + gg->register_fcns("DAC-"+name, make_gain_fcns_from_subtree(_tree->subtree(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains" / name)), 1 /* high prio */); } BOOST_FOREACH(const std::string &name, _tree->list(tx_rf_fe_root(chan) / "gains")){ gg->register_fcns(name, make_gain_fcns_from_subtree(_tree->subtree(tx_rf_fe_root(chan) / "gains" / name)), 0 /* low prio */); diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp index c82569ea3..7383c9833 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.cpp +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -27,6 +27,7 @@ #include <boost/format.hpp> #include <boost/tuple/tuple.hpp> #include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> #include <boost/assign/list_of.hpp> #include <iomanip> @@ -375,6 +376,12 @@ double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq) void usrp1_codec_ctrl_impl::set_duc_freq(double freq, double rate) { double codec_rate = rate * 2; + + //correct for outside of rate (wrap around) + freq = std::fmod(freq, rate); + if (std::abs(freq) > rate/2.0) + freq -= boost::math::sign(freq)*rate; + double coarse_freq = coarse_tune(codec_rate, freq); double fine_freq = fine_tune(codec_rate / 4, freq - coarse_freq); diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index d256df660..1d8b9bd76 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -73,8 +73,8 @@ public: /* NOP */ } - void commit(size_t size){ - if (size != 0) this->_commit_cb(_curr_buff, _next_buff, size); + void release(void){ + this->_commit_cb(_curr_buff, _next_buff, size()); } sptr get_new( @@ -83,13 +83,13 @@ public: ){ _curr_buff = curr_buff; _next_buff = next_buff; - return make_managed_buffer(this); + return make(this, + _curr_buff.buff->cast<char *>() + _curr_buff.offset, + _curr_buff.buff->size() - _curr_buff.offset + ); } private: - void *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;} - size_t get_size(void) const{return _curr_buff.buff->size() - _curr_buff.offset;} - offset_send_buffer _curr_buff, _next_buff; commit_cb_type _commit_cb; }; diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 30986ac66..a5e51b7d2 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -89,11 +89,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) usrp1_fw_image = find_image_path(hint.get("fw", "usrp1_fw.ihx")); } catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate USRP1 firmware.\n" - "Please install the images package.\n" - ); - return usrp1_addrs; + UHD_MSG(warning) << boost::format("Could not locate USRP1 firmware. %s") % print_images_error(); } UHD_LOG << "USRP1 firmware image: " << usrp1_fw_image << std::endl; @@ -121,7 +117,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom(*fx2_ctrl, mboard_eeprom_t::MAP_B000); + const mboard_eeprom_t mb_eeprom(*fx2_ctrl, USRP1_EEPROM_MAP_KEY); device_addr_t new_addr; new_addr["type"] = "usrp1"; new_addr["name"] = mb_eeprom["name"]; @@ -214,14 +210,20 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree = property_tree::make(); _tree->create<std::string>("/name").set("USRP1 Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set("USRP1 (Classic)"); + _tree->create<std::string>(mb_path / "name").set("USRP1"); _tree->create<std::string>(mb_path / "load_eeprom") .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); //////////////////////////////////////////////////////////////////// + // create user-defined control objects + //////////////////////////////////////////////////////////////////// + _tree->create<std::pair<boost::uint8_t, boost::uint32_t> >(mb_path / "user" / "regs") + .subscribe(boost::bind(&usrp1_impl::set_reg, this, _1)); + + //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B000); + const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&usrp1_impl::set_mb_eeprom, this, _1)); @@ -358,6 +360,9 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ tx_db_eeprom.load(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A) : (I2C_ADDR_TX_B)); gdb_eeprom.load(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A ^ 5) : (I2C_ADDR_TX_B ^ 5)); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(mb_path / "rx_frontends" / db / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "rx_eeprom") .set(rx_db_eeprom) @@ -452,7 +457,7 @@ bool usrp1_impl::has_tx_halfband(void){ * Properties callback methods below **********************************************************************/ void usrp1_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B000); + mb_eeprom.commit(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); } void usrp1_impl::set_db_eeprom(const std::string &db, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -501,3 +506,8 @@ std::complex<double> usrp1_impl::set_rx_dc_offset(const std::string &db, const s return std::complex<double>(double(i_off) * (1ul << 31), double(q_off) * (1ul << 31)); } + +void usrp1_impl::set_reg(const std::pair<boost::uint8_t, boost::uint32_t> ®) +{ + _iface->poke32(reg.first, reg.second); +} diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 581f4cbdd..9461f0081 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -38,6 +38,8 @@ #ifndef INCLUDED_USRP1_IMPL_HPP #define INCLUDED_USRP1_IMPL_HPP +static const std::string USRP1_EEPROM_MAP_KEY = "B000"; + /*! * USRP1 implementation guts: * The implementation details are encapsulated here. @@ -133,6 +135,8 @@ private: void vandal_conquest_loop(void); + void set_reg(const std::pair<boost::uint8_t, boost::uint32_t> ®); + //handle the enables bool _rx_enabled, _tx_enabled; void enable_rx(bool enb){ diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index 10f7407b0..da39d9df1 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 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 @@ -32,5 +32,6 @@ IF(ENABLE_USRP2) ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp ) ENDIF(ENABLE_USRP2) diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index b2912c70c..0ae3b0bd8 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -35,8 +35,9 @@ static const bool enb_test_clk = false; */ class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{ public: - usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_clock_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; clk_regs = usrp2_clk_regs_t(_iface->get_rev()); _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; @@ -331,7 +332,7 @@ private: */ void write_reg(boost::uint8_t addr){ boost::uint32_t data = _ad9510_regs.get_write_reg(addr); - _iface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); + _spiface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); } /*! @@ -377,7 +378,7 @@ private: } usrp2_iface::sptr _iface; - + uhd::spi_iface::sptr _spiface; usrp2_clk_regs_t clk_regs; ad9510_regs_t _ad9510_regs; }; @@ -385,6 +386,6 @@ private: /*********************************************************************** * Public make function for the ad9510 clock control **********************************************************************/ -usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_clock_ctrl_impl(iface)); +usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_clock_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index 9ccbc959e..067e1e35d 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2012 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 @@ -29,10 +29,11 @@ public: /*! * Make a clock config for the ad9510 ic. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new clock control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Get the master clock frequency for the fpga. diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index 06bf83b15..b53c4d9df 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -32,8 +32,9 @@ using namespace uhd; */ class usrp2_codec_ctrl_impl : public usrp2_codec_ctrl{ public: - usrp2_codec_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_codec_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; //setup the ad9777 dac _ad9777_regs.x_1r_2r_mode = ad9777_regs_t::X_1R_2R_MODE_1R; @@ -189,11 +190,12 @@ private: ad9777_regs_t _ad9777_regs; ads62p44_regs_t _ads62p44_regs; usrp2_iface::sptr _iface; + uhd::spi_iface::sptr _spiface; void send_ad9777_reg(boost::uint8_t addr){ boost::uint16_t reg = _ad9777_regs.get_write_reg(addr); UHD_LOGV(always) << "send_ad9777_reg: " << std::hex << reg << std::endl; - _iface->write_spi( + _spiface->write_spi( SPI_SS_AD9777, spi_config_t::EDGE_RISE, reg, 16 ); @@ -201,7 +203,7 @@ private: void send_ads62p44_reg(boost::uint8_t addr) { boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr); - _iface->write_spi( + _spiface->write_spi( SPI_SS_ADS62P44, spi_config_t::EDGE_FALL, reg, 16 ); @@ -211,6 +213,6 @@ private: /*********************************************************************** * Public make function for the usrp2 codec control **********************************************************************/ -usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_codec_ctrl_impl(iface)); +usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_codec_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp index ca300e2b1..b0d815be2 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.hpp +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -28,10 +28,11 @@ public: /*! * Make a codec control for the DAC and ADC. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new codec control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Set the modulation mode for the DAC. diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index bc510c8a1..edd9ef242 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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,7 +16,7 @@ // #include "gpio_core_200.hpp" -#include "usrp2_iface.hpp" +#include <uhd/types/serial.hpp> #include "clock_ctrl.hpp" #include "usrp2_regs.hpp" //wishbone address constants #include <uhd/usrp/dboard_iface.hpp> @@ -35,7 +35,12 @@ using namespace boost::assign; class usrp2_dboard_iface : public dboard_iface{ public: - usrp2_dboard_iface(usrp2_iface::sptr iface, usrp2_clock_ctrl::sptr clock_ctrl); + usrp2_dboard_iface( + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, + usrp2_clock_ctrl::sptr clock_ctrl + ); ~usrp2_dboard_iface(void); special_props_t get_special_props(void){ @@ -79,7 +84,8 @@ public: ); private: - usrp2_iface::sptr _iface; + uhd::i2c_iface::sptr _i2c_iface; + uhd::spi_iface::sptr _spi_iface; usrp2_clock_ctrl::sptr _clock_ctrl; gpio_core_200::sptr _gpio; @@ -92,22 +98,28 @@ private: * Make Function **********************************************************************/ dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl ){ - return dboard_iface::sptr(new usrp2_dboard_iface(iface, clock_ctrl)); + return dboard_iface::sptr(new usrp2_dboard_iface(wb_iface, i2c_iface, spi_iface, clock_ctrl)); } /*********************************************************************** * Structors **********************************************************************/ usrp2_dboard_iface::usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl -){ - _iface = iface; - _clock_ctrl = clock_ctrl; - _gpio = gpio_core_200::make(_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); +): + _i2c_iface(i2c_iface), + _spi_iface(spi_iface), + _clock_ctrl(clock_ctrl) +{ + _gpio = gpio_core_200::make(wb_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); //reset the aux dacs _dac_regs[UNIT_RX] = ad5623_regs_t(); @@ -202,7 +214,7 @@ void usrp2_dboard_iface::write_spi( boost::uint32_t data, size_t num_bits ){ - _iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); + _spi_iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); } boost::uint32_t usrp2_dboard_iface::read_write_spi( @@ -211,18 +223,18 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi( boost::uint32_t data, size_t num_bits ){ - return _iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); + return _spi_iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); } /*********************************************************************** * I2C **********************************************************************/ void usrp2_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ - return _iface->write_i2c(addr, bytes); + return _i2c_iface->write_i2c(addr, bytes); } byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ - return _iface->read_i2c(addr, num_bytes); + return _i2c_iface->read_i2c(addr, num_bytes); } /*********************************************************************** @@ -233,7 +245,7 @@ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){ (UNIT_RX, SPI_SS_RX_DAC) (UNIT_TX, SPI_SS_TX_DAC) ; - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_dac[unit], spi_config_t::EDGE_FALL, _dac_regs[unit].get_reg(), 24 ); @@ -281,11 +293,11 @@ double usrp2_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which){ } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn //write and read spi - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ); - ad7922_regs.set_reg(boost::uint16_t(_iface->read_spi( + ad7922_regs.set_reg(boost::uint16_t(_spi_iface->read_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ))); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 0babf7445..7e3de1497 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -30,13 +30,20 @@ extern "C" { #endif //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 9 -#define USRP2_FW_COMPAT_NUM 11 -#define USRP2_FW_VER_MINOR 2 +#define USRP2_FPGA_COMPAT_NUM 10 +#define USRP2_FW_COMPAT_NUM 12 +#define USRP2_FW_VER_MINOR 3 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 +typedef struct{ + uint32_t sequence; + uint32_t vrt_hdr; + uint32_t ip_addr; + uint32_t udp_port; +} usrp2_stream_ctrl_t; + // udp ports for the usrp2 communication // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 @@ -44,12 +51,14 @@ extern "C" { #define USRP2_UDP_RX_DSP0_PORT 49156 #define USRP2_UDP_TX_DSP0_PORT 49157 #define USRP2_UDP_RX_DSP1_PORT 49158 +#define USRP2_UDP_FIFO_CRTL_PORT 49159 #define USRP2_UDP_UART_BASE_PORT 49170 #define USRP2_UDP_UART_GPS_PORT 49172 // Map for virtual firmware regs (not very big so we can keep it here for now) #define U2_FW_REG_LOCK_TIME 0 #define U2_FW_REG_LOCK_GPID 1 +#define U2_FW_REG_HAS_GPSDO 3 #define U2_FW_REG_VER_MINOR 7 //////////////////////////////////////////////////////////////////////// @@ -65,6 +74,8 @@ extern "C" { //////////////////////////////////////////////////////////////////////// #define USRP2_EE_MBOARD_REV 0x00 //2 bytes, little-endian (historic, don't blame me) #define USRP2_EE_MBOARD_MAC_ADDR 0x02 //6 bytes +#define USRP2_EE_MBOARD_GATEWAY 0x38 //uint32, big-endian +#define USRP2_EE_MBOARD_SUBNET 0x08 //uint32, big-endian #define USRP2_EE_MBOARD_IP_ADDR 0x0C //uint32, big-endian #define USRP2_EE_MBOARD_BOOTLOADER_FLAGS 0xF7 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index d32ffb62c..e06cf8f6f 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -21,6 +21,7 @@ #include "../../transport/super_send_packet_handler.hpp" #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" +#include "fw_common.h" #include <uhd/utils/log.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> @@ -31,6 +32,7 @@ #include <boost/thread/thread.hpp> #include <boost/format.hpp> #include <boost/bind.hpp> +#include <boost/asio.hpp> #include <boost/thread/mutex.hpp> #include <boost/make_shared.hpp> #include <iostream> @@ -361,6 +363,61 @@ bool usrp2_impl::recv_async_msg( } /*********************************************************************** + * Stream destination programmer + **********************************************************************/ +void usrp2_impl::program_stream_dest( + zero_copy_if::sptr &xport, const uhd::stream_args_t &args +){ + //perform an initial flush of transport + while (xport->get_recv_buff(0.0)){} + + //program the stream command + usrp2_stream_ctrl_t stream_ctrl = usrp2_stream_ctrl_t(); + stream_ctrl.sequence = uhd::htonx(boost::uint32_t(0 /* don't care seq num */)); + stream_ctrl.vrt_hdr = uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER)); + + //user has provided an alternative address and port for destination + if (args.args.has_key("addr") and args.args.has_key("port")){ + UHD_MSG(status) << boost::format( + "Programming streaming destination for custom address.\n" + "IPv4 Address: %s, UDP Port: %s\n" + ) % args.args["addr"] % args.args["port"] << std::endl; + + asio::io_service io_service; + asio::ip::udp::resolver resolver(io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), args.args["addr"], args.args["port"]); + asio::ip::udp::endpoint endpoint = *resolver.resolve(query); + stream_ctrl.ip_addr = uhd::htonx(boost::uint32_t(endpoint.address().to_v4().to_ulong())); + stream_ctrl.udp_port = uhd::htonx(boost::uint32_t(endpoint.port())); + + for (size_t i = 0; i < 3; i++){ + UHD_MSG(status) << "ARP attempt " << i << std::endl; + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)); + send_buff.reset(); + boost::this_thread::sleep(boost::posix_time::milliseconds(300)); + managed_recv_buffer::sptr recv_buff = xport->get_recv_buff(0.0); + if (recv_buff and recv_buff->size() >= sizeof(boost::uint32_t)){ + const boost::uint32_t result = uhd::ntohx(recv_buff->cast<const boost::uint32_t *>()[0]); + if (result == 0){ + UHD_MSG(status) << "Success! " << std::endl; + return; + } + } + } + throw uhd::runtime_error("Device failed to ARP when programming alternative streaming destination."); + } + + else{ + //send the partial stream control without destination + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)/2); + } +} + +/*********************************************************************** * Receive streamer **********************************************************************/ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ @@ -406,6 +463,7 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far; _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _mbc[mb].rx_dsps[dsp]->setup(args); + this->program_stream_dest(_mbc[mb].rx_dsp_xports[dsp], args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 ), true /*flush*/); diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp new file mode 100644 index 000000000..efb88eb82 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp @@ -0,0 +1,244 @@ +// +// Copyright 2012 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 "usrp2_regs.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include "usrp2_fifo_ctrl.hpp" +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/asio.hpp> //htonl +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::transport; + +static const size_t POKE32_CMD = (1 << 8); +static const size_t PEEK32_CMD = 0; +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const boost::uint32_t MAX_SEQS_OUT = 15; + +#define SPI_DIV SR_SPI_CORE + 0 +#define SPI_CTRL SR_SPI_CORE + 1 +#define SPI_DATA SR_SPI_CORE + 2 +#define SPI_READBACK 0 +// spi clock rate = master_clock/(div+1)/2 (10MHz in this case) +#define SPI_DIVIDER 4 + +class usrp2_fifo_ctrl_impl : public usrp2_fifo_ctrl{ +public: + + usrp2_fifo_ctrl_impl(zero_copy_if::sptr xport): + _xport(xport), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT) + { + while (_xport->get_recv_buff(0.0)){} //flush + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + this->init_spi(); + } + + ~usrp2_fifo_ctrl_impl(void){ + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + ) + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(wb_addr_type addr, boost::uint32_t data){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt((addr - SETTING_REGS_BASE)/4, data, POKE32_CMD); + + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + } + + boost::uint32_t peek32(wb_addr_type addr){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt((addr - READBACK_BASE)/4, 0, PEEK32_CMD); + + return this->wait_for_ack(_seq_out); + } + + /******************************************************************* + * Peek and poke 16 bit not implemented + ******************************************************************/ + void poke16(wb_addr_type, boost::uint16_t){ + throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module"); + } + + boost::uint16_t peek16(wb_addr_type){ + throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module"); + } + + /******************************************************************* + * FIFO controlled SPI implementation + ******************************************************************/ + void init_spi(void){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + _ctrl_word_cache = 0; // force update first time around + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3ff) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word){ + this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + _ctrl_word_cache = ctrl_word; + } + + //send data word + this->send_pkt(SPI_DATA, data_out, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + //conditional readback + if (readback){ + this->send_pkt(SPI_READBACK, 0, PEEK32_CMD); + return this->wait_for_ack(_seq_out); + } + + return 0; + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time){ + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate){ + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){ + managed_send_buffer::sptr buff = _xport->get_send_buff(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *trans = buff->cast<boost::uint32_t *>(); + trans[0] = htonl(++_seq_out); + boost::uint32_t *pkt = trans + 1; + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = _seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.has_sid = false; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + vrt::if_hdr_pack_be(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = htonl(ctrl_word); + pkt[packet_info.num_header_words32+1] = htonl(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32+1)); + } + + UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){ + if (((i0 ^ i1) & 0x8000) == 0) //same sign bits + return boost::uint16_t(i0) < boost::uint16_t(i1); + return boost::int16_t(i1 - i0) > 0; + } + + UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){ + + while (wraparound_lt16(_seq_ack, seq_to_ack)){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(_timeout); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + vrt::if_hdr_unpack_be(pkt, packet_info); + _seq_ack = ntohl(pkt[packet_info.num_header_words32+0]) >> 16; + if (_seq_ack == seq_to_ack){ + return ntohl(pkt[packet_info.num_header_words32+1]); + } + } + + return 0; + } + + zero_copy_if::sptr _xport; + boost::mutex _mutex; + boost::uint16_t _seq_out; + boost::uint16_t _seq_ack; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + boost::uint32_t _ctrl_word_cache; +}; + + +usrp2_fifo_ctrl::sptr usrp2_fifo_ctrl::make(zero_copy_if::sptr xport){ + return sptr(new usrp2_fifo_ctrl_impl(xport)); +} diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp new file mode 100644 index 000000000..b48d05aa2 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp @@ -0,0 +1,47 @@ +// +// Copyright 2012 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 INCLUDED_USRP2_FIFO_CTRL_HPP +#define INCLUDED_USRP2_FIFO_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + +/*! + * The usrp2 FIFO control class: + * Provide high-speed peek/poke interface. + */ +class usrp2_fifo_ctrl : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<usrp2_fifo_ctrl> sptr; + + //! Make a new FIFO control object + static sptr make(uhd::transport::zero_copy_if::sptr xport); + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_USRP2_FIFO_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 123910166..f0b2a90a6 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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,11 +16,13 @@ // #include "usrp2_regs.hpp" +#include "usrp2_impl.hpp" #include "fw_common.h" #include "usrp2_iface.hpp" #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> +#include <uhd/utils/images.hpp> #include <uhd/utils/safe_call.hpp> #include <uhd/types/dict.hpp> #include <boost/thread.hpp> @@ -31,12 +33,14 @@ #include <boost/bind.hpp> #include <boost/tokenizer.hpp> #include <boost/functional/hash.hpp> +#include <boost/filesystem.hpp> #include <algorithm> #include <iostream> using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; +namespace fs = boost::filesystem; static const double CTRL_RECV_TIMEOUT = 1.0; static const size_t CTRL_RECV_RETRIES = 3; @@ -105,7 +109,7 @@ public: throw uhd::runtime_error("firmware not responding"); _protocol_compat = ntohl(ctrl_data.proto_ver); - mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100); + mb_eeprom = mboard_eeprom_t(*this, USRP2_EEPROM_MAP_KEY); } ~usrp2_iface_impl(void){UHD_SAFE_CALL( @@ -118,12 +122,12 @@ public: void lock_device(bool lock){ if (lock){ - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); + this->pokefw(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); _lock_task = task::make(boost::bind(&usrp2_iface_impl::lock_task, this)); } else{ _lock_task.reset(); //shutdown the task - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, 0); //unlock + this->pokefw(U2_FW_REG_LOCK_TIME, 0); //unlock } } @@ -131,8 +135,8 @@ public: //never assume lock with fpga image mismatch if ((this->peek32(U2_REG_COMPAT_NUM_RB) >> 16) != USRP2_FPGA_COMPAT_NUM) return false; - boost::uint32_t lock_time = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_TIME); - boost::uint32_t lock_gpid = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_GPID); + boost::uint32_t lock_time = this->peekfw(U2_FW_REG_LOCK_TIME); + boost::uint32_t lock_gpid = this->peekfw(U2_FW_REG_LOCK_GPID); //may not be the right tick rate, but this is ok for locking purposes const boost::uint32_t lock_timeout_time = boost::uint32_t(3*100e6); @@ -148,7 +152,7 @@ public: void lock_task(void){ //re-lock in task - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, this->get_curr_time()); + this->pokefw(U2_FW_REG_LOCK_TIME, this->get_curr_time()); //sleep for a bit boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); } @@ -176,6 +180,16 @@ public: return this->get_reg<boost::uint16_t, USRP2_REG_ACTION_FPGA_PEEK16>(addr); } + void pokefw(wb_addr_type addr, boost::uint32_t data) + { + this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(addr, data); + } + + boost::uint32_t peekfw(wb_addr_type addr) + { + return this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(addr); + } + template <class T, usrp2_reg_action_t action> T get_reg(wb_addr_type addr, T data = 0){ //setup the out data @@ -311,8 +325,10 @@ public: "\nPlease update the firmware and FPGA images for your device.\n" "See the application notes for USRP2/N-Series for instructions.\n" "Expected protocol compatibility number %s, but got %d:\n" - "The firmware build is not compatible with the host code build." - ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) % compat)); + "The firmware build is not compatible with the host code build.\n" + "%s\n" + ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) + % compat % this->images_warn_help_message())); } if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(ctrl_data_in->seq) == _ctrl_seq_num){ return *ctrl_data_in; @@ -340,13 +356,13 @@ public: const std::string get_cname(void){ switch(this->get_rev()){ - case USRP2_REV3: return "USRP2-REV3"; - case USRP2_REV4: return "USRP2-REV4"; - case USRP_N200: return "USRP-N200"; - case USRP_N210: return "USRP-N210"; - case USRP_N200_R4: return "USRP-N200-REV4"; - case USRP_N210_R4: return "USRP-N210-REV4"; - case USRP_NXXX: return "USRP-N???"; + case USRP2_REV3: return "USRP2 r3"; + case USRP2_REV4: return "USRP2 r4"; + case USRP_N200: return "N200"; + case USRP_N210: return "N210"; + case USRP_N200_R4: return "N200r4"; + case USRP_N210_R4: return "N210r4"; + case USRP_NXXX: return "N???"; } UHD_THROW_INVALID_CODE_PATH(); } @@ -356,6 +372,59 @@ public: return str(boost::format("%u.%u") % _protocol_compat % minor); } + std::string images_warn_help_message(void){ + //determine the images names + std::string fw_image, fpga_image; + switch(this->get_rev()){ + case USRP2_REV3: fpga_image = "usrp2_fpga.bin"; fw_image = "usrp2_fw.bin"; break; + case USRP2_REV4: fpga_image = "usrp2_fpga.bin"; fw_image = "usrp2_fw.bin"; break; + case USRP_N200: fpga_image = "usrp_n200_r2_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break; + case USRP_N210: fpga_image = "usrp_n210_r2_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break; + case USRP_N200_R4: fpga_image = "usrp_n200_r4_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break; + case USRP_N210_R4: fpga_image = "usrp_n210_r4_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break; + default: break; + } + if (fw_image.empty() or fpga_image.empty()) return ""; + + //does your platform use sudo? + std::string sudo; + #if defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_MACOS) + sudo = "sudo "; + #endif + + + //look up the real FS path to the images + std::string fw_image_path, fpga_image_path; + try{ + fw_image_path = uhd::find_image_path(fw_image); + fpga_image_path = uhd::find_image_path(fpga_image); + } + catch(const std::exception &){ + return str(boost::format("Could not find %s and %s in your images path!\n%s") % fw_image % fpga_image % print_images_error()); + } + + //escape char for multi-line cmd + newline + indent? + #ifdef UHD_PLATFORM_WIN32 + const std::string ml = "^\n "; + #else + const std::string ml = "\\\n "; + #endif + + //create the images downloader and burner commands + const std::string images_downloader_cmd = str(boost::format("%s\"%s\"") % sudo % find_images_downloader()); + if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){ + const std::string card_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp2_card_burner.py").string(); + const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); + return str(boost::format("%s\n%s") % print_images_error() % card_burner_cmd); + } + else{ + const std::string addr = _ctrl_transport->get_recv_addr(); + const std::string net_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp_n2xx_net_burner.py").string(); + const std::string net_burner_cmd = str(boost::format("\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\" %s--addr=\"%s\"") % net_burner % ml % fpga_image_path % ml % fw_image_path % ml % addr); + return str(boost::format("%s\n%s") % print_images_error() % net_burner_cmd); + } + } + private: //this lovely lady makes it all possible udp_simple::sptr _ctrl_transport; diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index 9aa1a16aa..ed4de02d5 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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,12 @@ public: */ static sptr make(uhd::transport::udp_simple::sptr ctrl_transport); + //! poke a register in the virtual fw table + virtual void pokefw(wb_addr_type addr, boost::uint32_t data) = 0; + + //! peek a register in the virtual fw table + virtual boost::uint32_t peekfw(wb_addr_type addr) = 0; + //! The list of possible revision types enum rev_type { USRP2_REV3 = 3, @@ -69,6 +75,9 @@ public: //! A version string for firmware virtual const std::string get_fw_version_string(void) = 0; + //! Construct a helpful warning message for images + virtual std::string images_warn_help_message(void) = 0; + //motherboard eeprom map structure uhd::usrp::mboard_eeprom_t mb_eeprom; }; diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index e6e8ca675..21f166aa1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -368,8 +368,9 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ "\nPlease update the firmware and FPGA images for your device.\n" "See the application notes for USRP2/N-Series for instructions.\n" "Expected FPGA compatibility number %d, but got %d:\n" - "The FPGA build is not compatible with the host code build." - ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_major)); + "The FPGA build is not compatible with the host code build.\n" + "%s\n" + ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_major % _mbc[mb].iface->images_warn_help_message())); } _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); @@ -391,8 +392,28 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].tx_dsp_xport = make_xport( addr, BOOST_STRINGIZE(USRP2_UDP_TX_DSP0_PORT), device_args_i, "send" ); + UHD_LOG << "Making transport for Control..." << std::endl; + _mbc[mb].fifo_ctrl_xport = make_xport( + addr, BOOST_STRINGIZE(USRP2_UDP_FIFO_CRTL_PORT), device_addr_t(), "" + ); //set the filter on the router to take dsp data from this port - _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, USRP2_UDP_TX_DSP0_PORT); + _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, (USRP2_UDP_FIFO_CRTL_PORT << 16) | USRP2_UDP_TX_DSP0_PORT); + + //create the fifo control interface for high speed register access + _mbc[mb].fifo_ctrl = usrp2_fifo_ctrl::make(_mbc[mb].fifo_ctrl_xport); + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _mbc[mb].wbiface = _mbc[mb].fifo_ctrl; + _mbc[mb].spiface = _mbc[mb].fifo_ctrl; + break; + default: + _mbc[mb].wbiface = _mbc[mb].iface; + _mbc[mb].spiface = _mbc[mb].iface; + break; + } //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -404,7 +425,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ //////////////////////////////////////////////////////////////// // create clock control objects //////////////////////////////////////////////////////////////// - _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface); + _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock)) .subscribe(boost::bind(&usrp2_impl::update_tick_rate, this, _1)); @@ -416,7 +437,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<int>(rx_codec_path / "gains"); //phony property so this dir exists _tree->create<int>(tx_codec_path / "gains"); //phony property so this dir exists - _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface); + _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: @@ -442,19 +463,48 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ } _tree->create<std::string>(tx_codec_path / "name").set("ad9777"); - //////////////////////////////////////////////////////////////// - // create gpsdo control objects - //////////////////////////////////////////////////////////////// - if (_mbc[mb].iface->mb_eeprom["gpsdo"] == "internal"){ - _mbc[mb].gps = gps_ctrl::make(udp_simple::make_uart(udp_simple::make_connected( - addr, BOOST_STRINGIZE(USRP2_UDP_UART_GPS_PORT) - ))); - if(_mbc[mb].gps->gps_detected()) { - BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()){ + //////////////////////////////////////////////////////////////////// + // Create the GPSDO control + //////////////////////////////////////////////////////////////////// + static const boost::uint32_t dont_look_for_gpsdo = 0x1234abcdul; + + //disable check for internal GPSDO when not the following: + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + break; + default: + _mbc[mb].iface->pokefw(U2_FW_REG_HAS_GPSDO, dont_look_for_gpsdo); + } + + //otherwise if not disabled, look for the internal GPSDO + if (_mbc[mb].iface->peekfw(U2_FW_REG_HAS_GPSDO) != dont_look_for_gpsdo) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try{ + _mbc[mb].gps = gps_ctrl::make(udp_simple::make_uart(udp_simple::make_connected( + addr, BOOST_STRINGIZE(USRP2_UDP_UART_GPS_PORT) + ))); + } + catch(std::exception &e){ + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) + { + UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()) + { _tree->create<sensor_value_t>(mb_path / "sensors" / name) .publish(boost::bind(&gps_ctrl::get_sensor, _mbc[mb].gps, name)); } } + else + { + UHD_MSG(status) << "not found" << std::endl; + _mbc[mb].iface->pokefw(U2_FW_REG_HAS_GPSDO, dont_look_for_gpsdo); + } } //////////////////////////////////////////////////////////////// @@ -469,10 +519,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create frontend control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_fe = rx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_FRONT) ); _mbc[mb].tx_fe = tx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_FRONT) ); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") @@ -503,10 +553,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create rx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true )); _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true )); for (size_t dspno = 0; dspno < _mbc[mb].rx_dsps.size(); dspno++){ _mbc[mb].rx_dsps[dspno]->set_link_rate(USRP2_LINK_RATE_BPS); @@ -531,7 +581,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].tx_dsp = tx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID ); _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -565,7 +615,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ time64_rb_bases.rb_hi_pps = U2_REG_TIME64_HI_RB_PPS; time64_rb_bases.rb_lo_pps = U2_REG_TIME64_LO_RB_PPS; _mbc[mb].time64 = time64_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1)); @@ -583,13 +633,26 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); + std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); + //plug timed commands into tree here + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_time, _mbc[mb].fifo_ctrl, _1)); + default: break; //otherwise, do not register + } + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_tick_rate, _mbc[mb].fifo_ctrl, _1)); //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _mbc[mb].user = user_settings_core_200::make(_mbc[mb].iface, U2_REG_SR_ADDR(SR_USER_REGS)); + _mbc[mb].user = user_settings_core_200::make(_mbc[mb].wbiface, U2_REG_SR_ADDR(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1)); @@ -603,6 +666,9 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ tx_db_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB); gdb_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB ^ 5); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(rx_fe_path / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom") .set(rx_db_eeprom) @@ -615,7 +681,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1)); //create a new dboard interface and manager - _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock); + _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].wbiface, _mbc[mb].iface/*i2c*/, _mbc[mb].spiface, _mbc[mb].clock); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface); _mbc[mb].dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -657,10 +723,11 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _tree->access<std::string>(root / "time_source/value").set("none"); //GPS installed: use external ref, time, and init time spec - if (_mbc[mb].gps.get() and _mbc[mb].gps->gps_detected()){ + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()){ + _mbc[mb].time64->enable_gpsdo(); UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(root / "time_source/value").set("external"); - _tree->access<std::string>(root / "clock_source/value").set("external"); + _tree->access<std::string>(root / "time_source/value").set("gpsdo"); + _tree->access<std::string>(root / "clock_source/value").set("gpsdo"); UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; _mbc[mb].time64->set_time_next_pps(time_spec_t(time_t(_mbc[mb].gps->get_sensor("gps_time").to_int()+1))); } @@ -675,7 +742,7 @@ usrp2_impl::~usrp2_impl(void){UHD_SAFE_CALL( )} void usrp2_impl::set_mb_eeprom(const std::string &mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*(_mbc[mb].iface), mboard_eeprom_t::MAP_N100); + mb_eeprom.commit(*(_mbc[mb].iface), USRP2_EEPROM_MAP_KEY); } void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -685,12 +752,12 @@ void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, c } sensor_value_t usrp2_impl::get_mimo_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; return sensor_value_t("MIMO", lock, "locked", "unlocked"); } sensor_value_t usrp2_impl::get_ref_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; return sensor_value_t("Ref", lock, "locked", "unlocked"); } @@ -729,14 +796,16 @@ meta_range_t usrp2_impl::get_tx_dsp_freq_range(const std::string &mb){ } void usrp2_impl::update_clock_source(const std::string &mb, const std::string &source){ + //NOTICE: U2_REG_MISC_CTRL_CLOCK is on the wb clock, and cannot be set from fifo_ctrl //clock source ref 10mhz switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: case usrp2_iface::USRP_N200_R4: case usrp2_iface::USRP_N210_R4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); + else if (source == "gpsdo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); _mbc[mb].clock->enable_external_ref(true); //USRP2P has an internal 10MHz TCXO @@ -744,7 +813,7 @@ void usrp2_impl::update_clock_source(const std::string &mb, const std::string &s case usrp2_iface::USRP2_REV3: case usrp2_iface::USRP2_REV4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index e5065c02d..a6c0d87cf 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -18,7 +18,9 @@ #ifndef INCLUDED_USRP2_IMPL_HPP #define INCLUDED_USRP2_IMPL_HPP +#include "gpio_core_200.hpp" #include "usrp2_iface.hpp" +#include "usrp2_fifo_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" #include "rx_frontend_core_200.hpp" @@ -51,15 +53,13 @@ static const size_t mimo_clock_sync_delay_cycles = 138; static const size_t USRP2_SRAM_BYTES = size_t(1 << 20); static const boost::uint32_t USRP2_TX_ASYNC_SID = 2; static const boost::uint32_t USRP2_RX_SID_BASE = 3; +static const std::string USRP2_EEPROM_MAP_KEY = "N100"; -/*! - * Make a usrp2 dboard interface. - * \param iface the usrp2 interface object - * \param clk_ctrl the clock control object - * \return a sptr to a new dboard interface - */ +//! Make a usrp2 dboard interface. uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clk_ctrl ); @@ -82,6 +82,9 @@ private: uhd::property_tree::sptr _tree; struct mb_container_type{ usrp2_iface::sptr iface; + usrp2_fifo_ctrl::sptr fifo_ctrl; + uhd::spi_iface::sptr spiface; + wb_iface::sptr wbiface; usrp2_clock_ctrl::sptr clock; usrp2_codec_ctrl::sptr codec; uhd::gps_ctrl::sptr gps; @@ -95,6 +98,7 @@ private: user_settings_core_200::sptr user; std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports; uhd::transport::zero_copy_if::sptr tx_dsp_xport; + uhd::transport::zero_copy_if::sptr fifo_ctrl_xport; uhd::usrp::dboard_manager::sptr dboard_manager; uhd::usrp::dboard_iface::sptr dboard_iface; size_t rx_chan_occ, tx_chan_occ; @@ -129,6 +133,7 @@ private: double set_tx_dsp_freq(const std::string &, const double); uhd::meta_range_t get_tx_dsp_freq_range(const std::string &); void update_clock_source(const std::string &, const std::string &); + void program_stream_dest(uhd::transport::zero_copy_if::sptr &, const uhd::stream_args_t &); }; #endif /* INCLUDED_USRP2_IMPL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index e14798ecb..7fe83e709 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -36,10 +36,10 @@ // Setting register offsets //////////////////////////////////////////////////////////////////////// #define SR_MISC 0 // 7 regs -#define SR_SIMTIMER 8 // 2 +#define SR_USER_REGS 8 // 2 #define SR_TIME64 10 // 6 #define SR_BUF_POOL 16 // 4 -#define SR_USER_REGS 20 // 2 +#define SR_SPI_CORE 20 // 3 #define SR_RX_FRONT 24 // 5 #define SR_RX_CTRL0 32 // 9 #define SR_RX_DSP0 48 // 7 diff --git a/host/lib/utils/images.cpp b/host/lib/utils/images.cpp index a124cc208..654e31179 100644 --- a/host/lib/utils/images.cpp +++ b/host/lib/utils/images.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -17,16 +17,18 @@ #include <uhd/utils/images.hpp> #include <uhd/exception.hpp> +#include <uhd/utils/paths.hpp> #include <boost/foreach.hpp> #include <boost/filesystem.hpp> #include <vector> +#include <iostream> namespace fs = boost::filesystem; std::vector<fs::path> get_image_paths(void); //defined in paths.cpp /*********************************************************************** - * Find a image in the image paths + * Find an image in the image paths **********************************************************************/ std::string uhd::find_image_path(const std::string &image_name){ if (fs::exists(image_name)){ @@ -38,3 +40,15 @@ std::string uhd::find_image_path(const std::string &image_name){ } throw uhd::io_error("Could not find path for image: " + image_name); } + +std::string uhd::find_images_downloader(void){ + return fs::path((fs::path(get_pkg_data_path()) / "utils" / "uhd_images_downloader.py")).string(); +} + +std::string uhd::print_images_error(void){ + #ifdef UHD_PLATFORM_WIN32 + return "As an Administrator, please run:\n\n\"" + find_images_downloader() + "\""; + #else + return "Please run:\n\nsudo \"" + find_images_downloader() + "\""; + #endif +} diff --git a/host/lib/utils/log.cpp b/host/lib/utils/log.cpp index 31d11796e..d6d1786c7 100644 --- a/host/lib/utils/log.cpp +++ b/host/lib/utils/log.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2012 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 @@ -18,6 +18,7 @@ #include <uhd/utils/log.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/static.hpp> +#include <uhd/utils/paths.hpp> #include <boost/filesystem.hpp> #include <boost/format.hpp> #include <boost/thread/mutex.hpp> @@ -35,12 +36,6 @@ namespace boost{ namespace interprocess{ #else #include <boost/interprocess/sync/file_lock.hpp> #endif -#ifdef BOOST_MSVC -#define USE_GET_TEMP_PATH -#include <Windows.h> //GetTempPath -#endif -#include <stdio.h> //P_tmpdir -#include <cstdlib> //getenv #include <fstream> #include <sstream> #include <cctype> @@ -50,42 +45,6 @@ namespace pt = boost::posix_time; namespace ip = boost::interprocess; /*********************************************************************** - * Helper function to get the system's temporary path - **********************************************************************/ -static fs::path get_temp_path(void){ - const char *tmp_path = NULL; - - //try the official uhd temp path environment variable - tmp_path = std::getenv("UHD_TEMP_PATH"); - if (tmp_path != NULL) return tmp_path; - - //try the windows function if available - #ifdef USE_GET_TEMP_PATH - char lpBuffer[2048]; - if (GetTempPath(sizeof(lpBuffer), lpBuffer)) return lpBuffer; - #endif - - //try windows environment variables - tmp_path = std::getenv("TMP"); - if (tmp_path != NULL) return tmp_path; - - tmp_path = std::getenv("TEMP"); - if (tmp_path != NULL) return tmp_path; - - //try the stdio define if available - #ifdef P_tmpdir - return P_tmpdir; - #endif - - //try unix environment variables - tmp_path = std::getenv("TMPDIR"); - if (tmp_path != NULL) return tmp_path; - - //give up and use the unix default - return "/tmp"; -} - -/*********************************************************************** * Global resources for the logger **********************************************************************/ class log_resource_type{ @@ -119,7 +78,7 @@ public: void log_to_file(const std::string &log_msg){ boost::mutex::scoped_lock lock(_mutex); if (_file_lock == NULL){ - const std::string log_path = (get_temp_path() / "uhd.log").string(); + const std::string log_path = (fs::path(uhd::get_tmp_path()) / "uhd.log").string(); _file_stream.open(log_path.c_str(), std::fstream::out | std::fstream::app); _file_lock = new ip::file_lock(log_path.c_str()); } diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index 4fc877d5d..26fa6d1c7 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -26,6 +26,10 @@ #include <vector> #include <cstdlib> //getenv #include <cstdio> //P_tmpdir +#ifdef BOOST_MSVC +#define USE_GET_TEMP_PATH +#include <windows.h> //GetTempPath +#endif namespace fs = boost::filesystem; @@ -67,19 +71,20 @@ static std::vector<fs::path> get_env_paths(const std::string &var_name){ /*********************************************************************** * Get a list of special purpose paths **********************************************************************/ -static fs::path get_uhd_pkg_data_path(void){ - return fs::path(get_env_var("UHD_PKG_DATA_PATH", UHD_PKG_DATA_PATH)); +std::string uhd::get_pkg_data_path(void) +{ + return get_env_var("UHD_PKG_DATA_PATH", UHD_PKG_DATA_PATH); } std::vector<fs::path> get_image_paths(void){ std::vector<fs::path> paths = get_env_paths("UHD_IMAGE_PATH"); - paths.push_back(get_uhd_pkg_data_path() / "images"); + paths.push_back(fs::path(uhd::get_pkg_data_path()) / "images"); return paths; } std::vector<fs::path> get_module_paths(void){ std::vector<fs::path> paths = get_env_paths("UHD_MODULE_PATH"); - paths.push_back(get_uhd_pkg_data_path() / "modules"); + paths.push_back(fs::path(uhd::get_pkg_data_path()) / "modules"); return paths; } @@ -87,13 +92,35 @@ std::vector<fs::path> get_module_paths(void){ * Implement the functions in paths.hpp **********************************************************************/ std::string uhd::get_tmp_path(void){ - const char *tmp_path = std::getenv("TMP"); + const char *tmp_path = NULL; + + //try the official uhd temp path environment variable + tmp_path = std::getenv("UHD_TEMP_PATH"); + if (tmp_path != NULL) return tmp_path; + + //try the windows function if available + #ifdef USE_GET_TEMP_PATH + char lpBuffer[2048]; + if (GetTempPath(sizeof(lpBuffer), lpBuffer)) return lpBuffer; + #endif + + //try windows environment variables + tmp_path = std::getenv("TMP"); if (tmp_path != NULL) return tmp_path; + tmp_path = std::getenv("TEMP"); + if (tmp_path != NULL) return tmp_path; + + //try the stdio define if available #ifdef P_tmpdir if (P_tmpdir != NULL) return P_tmpdir; #endif + //try unix environment variables + tmp_path = std::getenv("TMPDIR"); + if (tmp_path != NULL) return tmp_path; + + //give up and use the unix default return "/tmp"; } diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index 6b0ae53a9..4b0226e3d 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -19,6 +19,7 @@ #include <boost/test/unit_test.hpp> #include <boost/foreach.hpp> #include <boost/cstdint.hpp> +#include <boost/assign/list_of.hpp> #include <complex> #include <vector> #include <cstdlib> @@ -45,7 +46,9 @@ template <typename Range> static void loopback( convert::id_type &in_id, convert::id_type &out_id, const Range &input, - Range &output + Range &output, + const int prio_in = -1, + const int prio_out = -1 ){ //item32 is largest device type std::vector<boost::uint32_t> interm(nsamps); @@ -54,12 +57,12 @@ template <typename Range> static void loopback( std::vector<void *> output0(1, &interm[0]), output1(1, &output[0]); //convert to intermediate type - convert::converter::sptr c0 = convert::get_converter(in_id)(); + convert::converter::sptr c0 = convert::get_converter(in_id, prio_in)(); c0->set_scalar(32767.); c0->conv(input0, output0, nsamps); //convert back to host type - convert::converter::sptr c1 = convert::get_converter(out_id)(); + convert::converter::sptr c1 = convert::get_converter(out_id, prio_out)(); c1->set_scalar(1/32767.); c1->conv(input1, output1, nsamps); } @@ -133,10 +136,21 @@ static void test_convert_types_for_floats( convert::id_type out_id = id; std::swap(out_id.input_format, out_id.output_format); std::swap(out_id.num_inputs, out_id.num_outputs); - loopback(nsamps, in_id, out_id, input, output); - for (size_t i = 0; i < nsamps; i++){ - MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(1./32767)); - MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(1./32767)); + + //make a list of all prio: best/generic combos + typedef std::pair<int, int> int_pair_t; + std::vector<int_pair_t> prios = boost::assign::list_of + (int_pair_t(0, 0)) (int_pair_t(-1, 0)) + (int_pair_t(0, -1)) (int_pair_t(-1, -1)) + ; + + //loopback foreach prio combo (generic vs best) + BOOST_FOREACH(const int_pair_t &prio, prios){ + loopback(nsamps, in_id, out_id, input, output, prio.first, prio.second); + for (size_t i = 0; i < nsamps; i++){ + MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(1./32767)); + MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(1./32767)); + } } } diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 9b45d7016..5a40029dc 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -50,16 +50,11 @@ public: sptr get_new(boost::shared_array<char> mem, size_t len){ _mem = mem; - _len = len; - return make_managed_buffer(this); + return make(this, _mem.get(), len); } private: - const void *get_buff(void) const{return _mem.get();} - size_t get_size(void) const{return _len;} - boost::shared_array<char> _mem; - size_t _len; }; /*********************************************************************** @@ -89,8 +84,8 @@ public: uhd::transport::managed_recv_buffer::sptr get_recv_buff(double){ if (_mems.empty()) return uhd::transport::managed_recv_buffer::sptr(); //timeout - _mrbs.push_back(dummy_mrb()); - uhd::transport::managed_recv_buffer::sptr mrb = _mrbs.back().get_new(_mems.front(), _lens.front()); + _mrbs.push_back(boost::shared_ptr<dummy_mrb>(new dummy_mrb())); + uhd::transport::managed_recv_buffer::sptr mrb = _mrbs.back()->get_new(_mems.front(), _lens.front()); _mems.pop_front(); _lens.pop_front(); return mrb; @@ -99,7 +94,7 @@ public: private: std::list<boost::shared_array<char> > _mems; std::list<size_t> _lens; - std::list<dummy_mrb> _mrbs; //list means no-realloc + std::vector<boost::shared_ptr<dummy_mrb> > _mrbs; std::string _end; }; diff --git a/host/tests/sph_send_test.cpp b/host/tests/sph_send_test.cpp index c31399d12..603b36c85 100644 --- a/host/tests/sph_send_test.cpp +++ b/host/tests/sph_send_test.cpp @@ -31,23 +31,17 @@ **********************************************************************/ class dummy_msb : public uhd::transport::managed_send_buffer{ public: - void commit(size_t len){ - if (len == 0) return; - *_len = len; + void release(void){ + //NOP } sptr get_new(boost::shared_array<char> mem, size_t *len){ _mem = mem; - _len = len; - return make_managed_buffer(this); + return make(this, mem.get(), *len); } private: - void *get_buff(void) const{return _mem.get();} - size_t get_size(void) const{return *_len;} - boost::shared_array<char> _mem; - size_t *_len; }; /*********************************************************************** @@ -74,17 +68,17 @@ public: } uhd::transport::managed_send_buffer::sptr get_send_buff(double){ - _msbs.push_back(dummy_msb()); + _msbs.push_back(boost::shared_ptr<dummy_msb>(new dummy_msb())); _mems.push_back(boost::shared_array<char>(new char[1000])); _lens.push_back(1000); - uhd::transport::managed_send_buffer::sptr mrb = _msbs.back().get_new(_mems.back(), &_lens.back()); + uhd::transport::managed_send_buffer::sptr mrb = _msbs.back()->get_new(_mems.back(), &_lens.back()); return mrb; } private: std::list<boost::shared_array<char> > _mems; std::list<size_t> _lens; - std::list<dummy_msb> _msbs; //list means no-realloc + std::vector<boost::shared_ptr<dummy_msb> > _msbs; std::string _end; }; diff --git a/host/usrp_e_utils/CMakeLists.txt b/host/usrp_e_utils/CMakeLists.txt deleted file mode 100644 index 0d6763174..000000000 --- a/host/usrp_e_utils/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# -# 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 -######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP-E Utils" ENABLE_USRP_E_UTILS OFF "LINUX" OFF) - -IF(ENABLE_USRP_E_UTILS) - ENABLE_LANGUAGE(C) - INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/e100) - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/e100/include) - - SET(usrp_e_utils_sources - usrp-e-loopback.cpp - usrp-e-wb-test.cpp - ) - - #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_LIB_DIR}/usrp_e_utils) - ENDFOREACH(util_source) - -ENDIF(ENABLE_USRP_E_UTILS) diff --git a/host/usrp_e_utils/common.hpp b/host/usrp_e_utils/common.hpp deleted file mode 100644 index 989aecd06..000000000 --- a/host/usrp_e_utils/common.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// 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 <sys/ioctl.h> //ioctl -#include <fcntl.h> //open, close - -#include <linux/usrp_e.h> -#include "e100_regs.hpp" - -static int fp; - -static inline 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 inline 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 inline 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 inline 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); -} diff --git a/host/usrp_e_utils/usrp-e-loopback.cpp b/host/usrp_e_utils/usrp-e-loopback.cpp deleted file mode 100644 index d4220adc3..000000000 --- a/host/usrp_e_utils/usrp-e-loopback.cpp +++ /dev/null @@ -1,298 +0,0 @@ -// -// 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 "common.hpp" -#include <cstdlib> -#include <cstdio> -#include <cstring> -#include <ctime> -#include <iostream> -#include <boost/thread/thread.hpp> -#include <boost/format.hpp> -#include <boost/cstdint.hpp> -#include <sys/mman.h> //mmap -#include <unistd.h> //getpagesize -#include <poll.h> //poll - -static const size_t bytes_per_frame = 2048; - -static const int poll_timeout_ms = 100; - -struct loopback_pkt_hdr_type{ - boost::uint32_t words32; - boost::uint32_t checksum; - boost::uint64_t seq_num; -}; - -struct loopback_pkt_type{ - loopback_pkt_hdr_type hdr; - boost::uint32_t data[(bytes_per_frame-sizeof(loopback_pkt_hdr_type))/sizeof(boost::uint32_t)]; -}; - -static ring_buffer_info (*recv_info)[]; -static ring_buffer_info (*send_info)[]; -static loopback_pkt_type (*recv_buff)[]; -static loopback_pkt_type (*send_buff)[]; - -static struct usrp_e_ring_buffer_size_t rb_size; - -static bool running = true; - -static boost::uint64_t seq_errors = 0; -static boost::uint64_t checksum_errors = 0; -static boost::uint64_t sent_words32 = 0; -static boost::uint64_t recvd_words32 = 0; - -static inline void print_pkt(const loopback_pkt_type &pkt){ - std::cout << std::endl; - std::cout << "pkt.hdr.words32 " << pkt.hdr.words32 << std::endl; - std::cout << "pkt.hdr.checksum " << pkt.hdr.checksum << std::endl; - std::cout << "pkt.hdr.seq_num " << pkt.hdr.seq_num << std::endl; -} - -boost::uint32_t my_checksum(void *buff, size_t size32){ - boost::uint32_t x = 0; - for (size_t i = 0; i < size32; i++){ - x += reinterpret_cast<boost::uint32_t *>(buff)[i]; - x ^= reinterpret_cast<boost::uint32_t *>(buff)[i]; - } - return x; -} - -/*********************************************************************** - * Read thread - recv frames and verify checksum - **********************************************************************/ -static void read_thread(void){ - std::cout << "start read thread... " << std::endl; - - boost::uint64_t seq_num = 0; - size_t index = 0; - - while (running){ - - loopback_pkt_type &pkt = (*recv_buff)[index]; - ring_buffer_info &info = (*recv_info)[index]; - - //wait for frame available - if (not (info.flags & RB_USER)){ - pollfd pfd; - pfd.fd = fp; - pfd.events = POLLIN; - if (poll(&pfd, 1, poll_timeout_ms) <= 0){ - std::cout << "Read poll timeout, exiting read thread!" << std::endl; - running = false; - return; - } - } - info.flags = RB_USER_PROCESS; - - //print_pkt(pkt); - - //handle checksum - const boost::uint32_t expected_checksum = pkt.hdr.checksum; - pkt.hdr.checksum = 0; //set to zero for calculation - const boost::uint32_t actual_checksum = my_checksum(&pkt, pkt.hdr.words32); - if (expected_checksum != actual_checksum){ - checksum_errors++; - std::cerr << "C"; - } - else{ - recvd_words32 += pkt.hdr.words32; - } - - //handle sequence - if (seq_num != pkt.hdr.seq_num){ - seq_errors++; - std::cerr << "S"; - } - seq_num = pkt.hdr.seq_num+1; - - //release the packet - info.flags = RB_KERNEL; - - //increment index and wrap around to zero - index++; - if (index == size_t(rb_size.num_rx_frames)) index = 0; - } - -} - -/*********************************************************************** - * Write thread - send frames and calculate checksum - **********************************************************************/ -static void write_thread(const size_t num_words32){ - std::cout << "start write thread... " << std::endl; - - srandom(std::time(NULL)); - - boost::uint64_t seq_num = 0; - size_t index = 0; - - //write into tmp and memcopy into pkt to avoid cache issues - loopback_pkt_type pkt_tmp; - - while (running){ - - ring_buffer_info &info = (*send_info)[index]; - - //wait for frame available - if (not (info.flags & RB_KERNEL)){ - pollfd pfd; - pfd.fd = fp; - pfd.events = POLLOUT; - if (poll(&pfd, 1, poll_timeout_ms) <= 0){ - std::cout << "Write poll timeout, exiting write thread!" << std::endl; - running = false; - return; - } - } - - //fill packet header and body - const boost::uint32_t seed = random(); - pkt_tmp.hdr.words32 = sizeof(pkt_tmp.hdr)/sizeof(boost::uint32_t) + num_words32; - pkt_tmp.hdr.checksum = 0; //set to zero for checksum() - pkt_tmp.hdr.seq_num = seq_num++; - for (size_t i = 0; i < num_words32; i++) pkt_tmp.data[i] = seed + i; - pkt_tmp.hdr.checksum = my_checksum(&pkt_tmp, pkt_tmp.hdr.words32); - sent_words32 += pkt_tmp.hdr.words32; - - loopback_pkt_type &pkt = (*send_buff)[index]; - std::memcpy(&pkt, &pkt_tmp, pkt_tmp.hdr.words32*sizeof(boost::uint32_t)); - - //print_pkt(pkt); - - //commit the packet - info.len = pkt_tmp.hdr.words32*sizeof(boost::uint32_t); - info.flags = RB_USER; - ::write(fp, NULL, 0); - - //increment index and wrap around to zero - index++; - if (index == size_t(rb_size.num_tx_frames)) index = 0; - } -} - -/*********************************************************************** - * Setup memory mapped ring buffer - **********************************************************************/ -static void setup_ring(void){ - std::cout << "setup memory mapped ring buffer... " << std::flush; - - //calculate various sizes - const size_t page_size = getpagesize(); - ioctl(fp, USRP_E_GET_RB_INFO, &rb_size); - const size_t 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); - - //call into memory map - void *mem = ::mmap(0, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0); - if (mem == MAP_FAILED) { - std::cerr << "mmap failed" << std::endl; - std::exit(-1); - } - - //calculate the memory offsets for info and buffers - const size_t recv_info_off = 0; - const size_t recv_buff_off = recv_info_off + (rb_size.num_pages_rx_flags * page_size); - const size_t send_info_off = recv_buff_off + (rb_size.num_rx_frames * page_size/2); - const size_t send_buff_off = send_info_off + (rb_size.num_pages_tx_flags * page_size); - - //set the internal pointers for info and buffers - typedef ring_buffer_info (*rbi_pta)[]; - typedef loopback_pkt_type (*pkt_pta)[]; - char *rb_ptr = reinterpret_cast<char *>(mem); - recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); - recv_buff = reinterpret_cast<pkt_pta>(rb_ptr + recv_buff_off); - send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); - send_buff = reinterpret_cast<pkt_pta>(rb_ptr + send_buff_off); - - std::cout << "done" << std::endl; -} - -/*********************************************************************** - * Main - **********************************************************************/ -#include <boost/program_options.hpp> - -int main(int argc, char *argv[]){ - - //variables to be set by po - double duration; - size_t nwords; - - //setup the program options - namespace po = boost::program_options; - po::options_description desc("Allowed options"); - desc.add_options() - ("help", "help message") - ("duration", po::value<double>(&duration)->default_value(10), "number of seconds to run loopback") - ("nwords", po::value<size_t>(&nwords)->default_value(400), "number of words32 to send per packet") - ; - 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-Loopback %s") % desc << std::endl; - return ~0; - } - - if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ - std::cerr << "Open failed" << std::endl; - return -1; - } - - //set the mode to loopback - poke16(E100_REG_MISC_XFER_RATE, (1<<8) | (1<<9)); - - //clear FIFO state in FPGA and kernel - poke32(E100_REG_CLEAR_FIFO, 0); - ::close(fp); - if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ - std::cerr << "Open failed" << std::endl; - return -1; - } - - //setup the ring buffer - setup_ring(); - - //spawn threads - boost::thread_group tg; - tg.create_thread(boost::bind(&read_thread)); - tg.create_thread(boost::bind(&write_thread, nwords)); - - const boost::system_time start_time = boost::get_system_time(); - const boost::system_time finish_time = start_time + boost::posix_time::milliseconds(long(duration*1000)); - while (boost::get_system_time() < finish_time){ - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - std::cerr << "."; - } - running = false; - tg.join_all(); - - std::cout << std::endl; - std::cout << "seq_errors " << seq_errors << std::endl; - std::cout << "checksum_errors " << checksum_errors << std::endl; - std::cout << "sent_words32 " << sent_words32 << std::endl; - std::cout << "recvd_words32 " << recvd_words32 << std::endl; - std::cout << "approx send rate " << (sent_words32/duration)/1e6 << "Msps" << std::endl; - std::cout << "approx recv rate " << (recvd_words32/duration)/1e6 << "Msps" << std::endl; - - ::close(fp); - return seq_errors + checksum_errors; -} diff --git a/host/usrp_e_utils/usrp-e-wb-test.cpp b/host/usrp_e_utils/usrp-e-wb-test.cpp deleted file mode 100644 index 2b793dc06..000000000 --- a/host/usrp_e_utils/usrp-e-wb-test.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// 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 "common.hpp" -#include <cstdlib> -#include <cstdio> -#include <ctime> -#include <iostream> - -static const size_t num_test_iters = 10000000; - -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(E100_REG_MISC_TEST, random_test16); - poke32(E100_REG_SR_MISC_TEST32, random_test32); - //poke32(E100_REG_TIME64_TICKS, 0); - //poke32(E100_REG_TIME64_IMM, 1); //immediate - //poke32(E100_REG_TIME64_SECS, random_secs); - - //read a bunch of registers - if ( - (peek16(E100_REG_MISC_TEST) == random_test16) and - (peek32(E100_REG_RB_MISC_TEST32) == random_test32) and -// (peek32(E100_REG_RB_TIME_NOW_SECS) == random_secs) and -// (peek32(E100_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 0ecd6b4e7..763bb47bc 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2012 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 @@ -38,6 +38,7 @@ ENDFOREACH(util_source) # Utilities that get installed into the share path ######################################################################## SET(util_share_sources + query_gpsdo_sensors.cpp usrp_burn_db_eeprom.cpp usrp_burn_mb_eeprom.cpp ) @@ -64,6 +65,17 @@ FOREACH(util_source ${util_share_sources}) INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) ENDFOREACH(util_source) +#UHD images downloader configuration +CONFIGURE_FILE( + ${CMAKE_CURRENT_SOURCE_DIR}/uhd_images_downloader.py.in + ${CMAKE_CURRENT_BINARY_DIR}/uhd_images_downloader.py +@ONLY) +INSTALL(PROGRAMS + ${CMAKE_CURRENT_BINARY_DIR}/uhd_images_downloader.py + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities +) + IF(ENABLE_USRP2) IF(WIN32 AND UHD_RELEASE_MODE) #include dd.exe FILE(DOWNLOAD @@ -76,8 +88,14 @@ IF(ENABLE_USRP2) COMPONENT utilities ) ENDIF(WIN32 AND UHD_RELEASE_MODE) + IF(LINUX) + INSTALL(PROGRAMS + usrp2_recovery.py + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities + ) + ENDIF(LINUX) INSTALL(PROGRAMS - usrp2_recovery.py usrp2_card_burner.py usrp2_card_burner_gui.py usrp_n2xx_net_burner.py diff --git a/host/utils/FastSendDatagramThreshold.reg b/host/utils/FastSendDatagramThreshold.reg Binary files differnew file mode 100755 index 000000000..c0665d09e --- /dev/null +++ b/host/utils/FastSendDatagramThreshold.reg diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp new file mode 100644 index 000000000..10792db7c --- /dev/null +++ b/host/utils/query_gpsdo_sensors.cpp @@ -0,0 +1,120 @@ +// +// Copyright 2012 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/paths.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/filesystem.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> +#include <boost/thread.hpp> +#include <string> +#include <cmath> + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + std::string args; + + //Set up program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "Specify a single USRP.") + ; + 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("Query GPSDO Sensors %s") % desc << std::endl; + return ~0; + } + + //Create a USRP device + std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string(); + + //Helpful notes + std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n"); + std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n"); + std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n"); + std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n"); + std::cout << boost::format("****************************************************************************************************************\n"); + + + //Verify GPS sensors are present (i.e. EEPROM has been burnt) + std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0); + + if(std::find(sensor_names.begin(), sensor_names.end(), "gps_locked") == sensor_names.end()){ + std::cout << boost::format("\ngps_locked sensor not found. This could mean that you have not installed the GPSDO correctly.\n\n"); + std::cout << boost::format("Visit this page if the problem persists:\n"); + std::cout << boost::format("http://files.ettus.com/uhd_docs/manual/html/gpsdo.html\n\n"); + exit(1); + } + + //Check for GPS lock + uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("gps_locked",0); + if(gps_locked.to_pp_string().find("unlocked") > 0){ + std::cout << boost::format("\nGPS does not have lock. Wait a few minutes and try again.\n"); + std::cout << boost::format("NMEA strings and device time may not be accurate until lock is achieved.\n\n"); + } + else std::cout << boost::format("GPS Locked\n"); + + //Check for 10 MHz lock + if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()){ + uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("ref_locked",0); + if(gps_locked.to_pp_string().find("unlocked") > 0){ + std::cout << boost::format("USRP NOT Locked to GPSDO 10 MHz Reference.\n"); + std::cout << boost::format("Double check installation instructions: https://www.ettus.com/content/files/gpsdo-kit_2.pdf\n\n"); + } + else std::cout << boost::format("USRP Locked to GPSDO 10 MHz Reference.\n"); + } + else std::cout << boost::format("ref_locked sensor not present on this board.\n"); + + //Check PPS and compare UHD device time to GPS time + uhd::sensor_value_t gps_time = usrp->get_mboard_sensor("gps_time"); + const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); + if(int(round(last_pps_time.get_real_secs())) == gps_time.to_int()) + { + std::cout << boost::format("GPS and UHD Device time are aligned.\n"); + } + else + { + std::cout << boost::format("\nGPS and UHD Device time are NOT aligned. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n"); + } + + //print NMEA strings + std::cout << boost::format("Printing available NMEA strings:\n"); + uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga"); + uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc"); + std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); + std::cout << boost::format("UHD Device time: %.0f seconds\n") % round(last_pps_time.get_real_secs()); + + //finished + std::cout << boost::format("\nDone!\n\n"); + + return 0; +} diff --git a/host/utils/uhd_images_downloader.py.in b/host/utils/uhd_images_downloader.py.in new file mode 100644 index 000000000..6aa619660 --- /dev/null +++ b/host/utils/uhd_images_downloader.py.in @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# Copyright 2012 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/>. +# + +from optparse import OptionParser +import os +import os.path +import shutil +import sys +import urllib2 +import zipfile + +if __name__ == "__main__": + + #Command line options + parser = OptionParser() + parser.add_option("--download-location", type="string", default="@CMAKE_INSTALL_PREFIX@/share/uhd/images", help="Set custom download location for images, [default=%default]") + parser.add_option("--buffer-size", type="int", default=8192, help="Set download buffer size, [default=%default]",) + (options, args) = parser.parse_args() + + #Configuring image download info + images_src = "@UHD_IMAGES_DOWNLOAD_SRC@" + filename = images_src.split("/")[-1] + + #Configuring image destination + cmake_install_prefix = "@CMAKE_INSTALL_PREFIX@" + if options.download_location != "": + images_dir = options.download_location + else: + images_dir = "@CMAKE_INSTALL_PREFIX@/share/uhd/images" + + u = urllib2.urlopen(images_src) + f = open(filename, "wb") + meta = u.info() + filesize = float(meta.getheaders("Content-Length")[0]) + + print "Downloading images from: %s" % images_src + + filesize_dl = 0.0 + + #Downloading file + while True: + buffer = u.read(options.buffer_size) + if not buffer: + break + + filesize_dl -= len(buffer) + f.write(buffer) + + status = r"%2.2f MB/%2.2f MB (%3.2f" % (-filesize_dl/1e6, filesize/1e6, (-filesize_dl*100.)/filesize) + r"%)" + status += chr(8)*(len(status)+1) + print status, + + f.close() + + #Extracting contents of zip file + if os.path.exists("tempdir"): + shutil.rmtree("tempdir") + os.mkdir("tempdir") + + images_zip = zipfile.ZipFile(filename) + images_zip.extractall("tempdir") + + #Removing images currently in images_dir + if os.path.exists(images_dir): + try: + shutil.rmtree(images_dir) + except: + sys.stderr.write("\nMake sure you have write permissions in the images directory.\n") + sys.exit(0) + + #Copying downloaded images into images_dir + shutil.copytree("tempdir/%s/share/uhd/images" % filename[:-4],images_dir) + + #Removing tempdir and zip file + shutil.rmtree("tempdir") + images_zip.close() + os.remove(filename) + + print "\nImages successfully installed to: %s" % images_dir diff --git a/host/utils/uhd_usrp_probe.cpp b/host/utils/uhd_usrp_probe.cpp index 1bd49a5ff..5b3702fb4 100644 --- a/host/utils/uhd_usrp_probe.cpp +++ b/host/utils/uhd_usrp_probe.cpp @@ -69,9 +69,9 @@ static std::string prop_names_to_pp_string(const std::vector<std::string> &prop_ return ss.str(); } -static std::string get_subdev_pp_string(const std::string &type, property_tree::sptr tree, const fs_path &path){ +static std::string get_frontend_pp_string(const std::string &type, property_tree::sptr tree, const fs_path &path){ std::stringstream ss; - ss << boost::format("%s Subdev: %s") % type % path.leaf() << std::endl; + ss << boost::format("%s Frontend: %s") % type % path.leaf() << std::endl; //ss << std::endl; ss << boost::format("Name: %s") % (tree->access<std::string>(path / "name").get()) << std::endl; @@ -123,7 +123,7 @@ static std::string get_dboard_pp_string(const std::string &type, property_tree:: if (not gdb_eeprom.serial.empty()) ss << boost::format("Serial: %s") % gdb_eeprom.serial << std::endl; } BOOST_FOREACH(const std::string &name, tree->list(path / (prefix + "_frontends"))){ - ss << make_border(get_subdev_pp_string(type, tree, path / (prefix + "_frontends") / name)); + ss << make_border(get_frontend_pp_string(type, tree, path / (prefix + "_frontends") / name)); } ss << make_border(get_codec_pp_string(type, tree, path.branch_path().branch_path() / (prefix + "_codecs") / path.leaf())); return ss.str(); @@ -137,6 +137,12 @@ static std::string get_mboard_pp_string(property_tree::sptr tree, const fs_path BOOST_FOREACH(const std::string &key, mb_eeprom.keys()){ if (not mb_eeprom[key].empty()) ss << boost::format("%s: %s") % key % mb_eeprom[key] << std::endl; } + if (tree->exists(path / "fw_version")){ + ss << "FW Version: " << tree->access<std::string>(path / "fw_version").get() << std::endl; + } + if (tree->exists(path / "fpga_version")){ + ss << "FPGA Version: " << tree->access<std::string>(path / "fpga_version").get() << std::endl; + } ss << std::endl; ss << "Time sources: " << prop_names_to_pp_string(tree->access<std::vector<std::string> >(path / "time_source" / "options").get()) << std::endl; ss << "Clock sources: " << prop_names_to_pp_string(tree->access<std::vector<std::string> >(path / "clock_source" / "options").get()) << std::endl; diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index 825d94d64..364b68bbe 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2011-2012 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 @@ -68,7 +68,13 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/tx_frontends/0"; const std::string tx_name = tree->access<std::string>(tx_fe_path / "name").get(); - if (tx_name.find("WBX") != std::string::npos or tx_name.find("SBX") != std::string::npos){ + if (tx_name.find("WBX") != std::string::npos){ + usrp->set_tx_gain(0); + } + else if (tx_name.find("SBX") != std::string::npos){ + usrp->set_tx_gain(0); + } + else if (tx_name.find("RFX") != std::string::npos){ usrp->set_tx_gain(0); } else{ @@ -77,7 +83,13 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/tx_frontends/0"; const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get(); - if (rx_name.find("WBX") != std::string::npos or rx_name.find("SBX") != std::string::npos){ + if (rx_name.find("WBX") != std::string::npos){ + usrp->set_rx_gain(25); + } + else if (rx_name.find("SBX") != std::string::npos){ + usrp->set_rx_gain(25); + } + else if (rx_name.find("RFX") != std::string::npos){ usrp->set_rx_gain(25); } else{ diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index f2cfb8ecf..8f16de501 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -222,7 +222,9 @@ def enumerate_devices(): pkt = sock.recv(UDP_MAX_XFER_BYTES) (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(pkt) if(pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG): - yield socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))) + use_addr = socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))) + burner = burner_socket(use_addr, True) + yield "%s (%s)" % (socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))), n2xx_revs[burner.get_hw_rev()][0]) except socket.timeout: still_goin = False @@ -230,12 +232,13 @@ def enumerate_devices(): # Burner class, holds a socket and send/recv routines ######################################################################## class burner_socket(object): - def __init__(self, addr): + def __init__(self, addr, quiet): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._quiet = quiet self._sock.settimeout(UDP_TIMEOUT) self._sock.connect((addr, UDP_FW_UPDATE_PORT)) self.set_callbacks(lambda *a: None, lambda *a: None) - self.init_update() #check that the device is there + self.init_update(quiet) #check that the device is there self.get_hw_rev() def set_callbacks(self, progress_cb, status_cb): @@ -247,13 +250,13 @@ class burner_socket(object): return self._sock.recv(UDP_MAX_XFER_BYTES) #just here to validate comms - def init_update(self): + def init_update(self,quiet): out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0) try: in_pkt = self.send_and_recv(out_pkt) except socket.timeout: raise Exception("No response from device") (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: - print("USRP-N2XX found.") + if not quiet: print("USRP-N2XX found.") else: raise Exception("Invalid reply received from device.") @@ -488,6 +491,7 @@ if __name__=='__main__': if options.list: print('Possible network devices:') print(' ' + '\n '.join(enumerate_devices())) + #enumerate_devices() exit() if not options.addr: raise Exception('no address specified') @@ -500,7 +504,7 @@ if __name__=='__main__': response = raw_input("""Type "yes" to continue, or anything else to quit: """) if response != "yes": sys.exit(0) - burner = burner_socket(addr=options.addr) + burner = burner_socket(addr=options.addr,quiet=False) if options.read: if options.fw: diff --git a/host/utils/usrp_n2xx_net_burner_gui.py b/host/utils/usrp_n2xx_net_burner_gui.py index e2b79e72c..a9150bd88 100755 --- a/host/utils/usrp_n2xx_net_burner_gui.py +++ b/host/utils/usrp_n2xx_net_burner_gui.py @@ -96,7 +96,11 @@ class DeviceEntryWidget(tkinter.Frame): tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack() self._hints = tkinter.Listbox(self) + self._hints_addrs_only = tkinter.Listbox(self) + self._hints.bind("<<ListboxSelect>>", self._listbox_cb) + self._hints_addrs_only.bind("<<ListboxSelect>>", self._listbox_cb) + self._reload_cb() self._hints.pack(expand=tkinter.YES, fill=tkinter.X) @@ -112,10 +116,11 @@ class DeviceEntryWidget(tkinter.Frame): self._hints.delete(0, tkinter.END) for hint in usrp_n2xx_net_burner.enumerate_devices(): self._hints.insert(tkinter.END, hint) + self._hints_addrs_only.insert(tkinter.END, hint.split(" (")[0]) def _listbox_cb(self, event): try: - sel = self._hints.get(self._hints.curselection()[0]) + sel = self._hints_addrs_only.get(self._hints.curselection()[0]) self._entry.delete(0, tkinter.END) self._entry.insert(0, sel) except Exception as e: print(e) @@ -196,7 +201,7 @@ class USRPN2XXNetBurnerApp(tkinter.Frame): self._disable_input() try: #make a new burner object and attempt the burner operation - burner = usrp_n2xx_net_burner.burner_socket(addr=addr) + burner = usrp_n2xx_net_burner.burner_socket(addr=addr,quiet=False) for (image_type, fw_img, fpga_img) in (('FPGA', '', fpga), ('Firmware', fw, '')): #setup callbacks that update the gui diff --git a/images/CMakeLists.txt b/images/CMakeLists.txt index a59568ef4..9a05f11f5 100644 --- a/images/CMakeLists.txt +++ b/images/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2012 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 diff --git a/images/Makefile b/images/Makefile index c9f7e7429..562aa76c0 100644 --- a/images/Makefile +++ b/images/Makefile @@ -95,13 +95,19 @@ ifdef HAS_XTCLSH _usrp_b100_fpga_dir = $(TOP_FPGA_DIR)/usrp2/top/B100 _usrp_b100_fpga_bin = $(BUILT_IMAGES_DIR)/usrp_b100_fpga.bin -IMAGES_LIST += $(_usrp_b100_fpga_bin) +_usrp_b100_fpga_2rx_bin = $(BUILT_IMAGES_DIR)/usrp_b100_fpga_2rx.bin +IMAGES_LIST += $(_usrp_b100_fpga_bin) $(_usrp_b100_fpga_2rx_bin) $(_usrp_b100_fpga_bin): $(GLOBAL_DEPS) cd $(_usrp_b100_fpga_dir) && make -f Makefile.B100 clean cd $(_usrp_b100_fpga_dir) && make -f Makefile.B100 bin cp $(_usrp_b100_fpga_dir)/build-B100/B100.bin $@ +$(_usrp_b100_fpga_2rx_bin): $(GLOBAL_DEPS) + cd $(_usrp_b100_fpga_dir) && make -f Makefile.B100_2RX clean + cd $(_usrp_b100_fpga_dir) && make -f Makefile.B100_2RX bin + cp $(_usrp_b100_fpga_dir)/build-B100_2RX/B100.bin $@ + endif ######################################################################## @@ -250,7 +256,7 @@ IMAGES_LIST += $(_usrp_e100_fpga_bin) $(_usrp_e100_fpga_bin): $(GLOBAL_DEPS) cd $(_usrp_e100_fpga_dir) && make -f Makefile.E100 clean cd $(_usrp_e100_fpga_dir) && make -f Makefile.E100 bin - cp $(_usrp_e100_fpga_dir)/build-E100/u1e.bin $@ + cp $(_usrp_e100_fpga_dir)/build-E100/E1x0.bin $@ endif @@ -266,7 +272,7 @@ IMAGES_LIST += $(_usrp_e110_fpga_bin) $(_usrp_e110_fpga_bin): $(GLOBAL_DEPS) cd $(_usrp_e110_fpga_dir) && make -f Makefile.E110 clean cd $(_usrp_e110_fpga_dir) && make -f Makefile.E110 bin - cp $(_usrp_e110_fpga_dir)/build-E110/u1e.bin $@ + cp $(_usrp_e110_fpga_dir)/build-E110/E1x0.bin $@ endif |