commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] r6728 - gnuradio/branches/features/inband-usb/usrp/hos


From: gnychis
Subject: [Commit-gnuradio] r6728 - gnuradio/branches/features/inband-usb/usrp/host/lib/inband
Date: Sun, 28 Oct 2007 19:19:40 -0600 (MDT)

Author: gnychis
Date: 2007-10-28 19:19:39 -0600 (Sun, 28 Oct 2007)
New Revision: 6728

Removed:
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/fake_usrp.cc
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/fake_usrp.h
Modified:
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/Makefile.am
   
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/qa_inband_usrp_server.cc
   
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_inband_usb_packet.cc
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx.cc
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.cc
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.h
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_server.cc
   gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_tx.cc
   
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_usb_interface.cc
Log:
Merging -r6706:6727 from developers/gnychis/inband


Modified: gnuradio/branches/features/inband-usb/usrp/host/lib/inband/Makefile.am
===================================================================
--- gnuradio/branches/features/inband-usb/usrp/host/lib/inband/Makefile.am      
2007-10-29 00:44:18 UTC (rev 6727)
+++ gnuradio/branches/features/inband-usb/usrp/host/lib/inband/Makefile.am      
2007-10-29 01:19:39 UTC (rev 6728)
@@ -78,7 +78,6 @@
        usrp_usb_interface.h            
 
 noinst_HEADERS =                       \
-       fake_usrp.h                     \
        qa_inband.h                     \
        qa_inband_packet_prims.h        \
        qa_inband_usrp_server.h         \

Deleted: gnuradio/branches/features/inband-usb/usrp/host/lib/inband/fake_usrp.cc

Deleted: gnuradio/branches/features/inband-usb/usrp/host/lib/inband/fake_usrp.h

Modified: 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/qa_inband_usrp_server.cc
===================================================================
--- 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/qa_inband_usrp_server.cc
 2007-10-29 00:44:18 UTC (rev 6727)
+++ 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/qa_inband_usrp_server.cc
 2007-10-29 01:19:39 UTC (rev 6728)
@@ -46,6 +46,8 @@
 
 static bool verbose = false;
 
+static pmt_t s_timeout = pmt_intern("%timeout");
+
 // 
----------------------------------------------------------------------------------------------
 
 class qa_alloc_top : public mb_mblock
@@ -903,10 +905,10 @@
 
   long d_rx_chan;
 
-  long d_got_response_recv;
+  bool d_got_response_recv;
 
-  long d_nmsg_to_recv;
-  long d_nmsg_recvd;
+  mb_time d_t0;
+  double d_delta_t;
 
  public:
   qa_rx_top(mb_runtime *runtime, const std::string &instance_name, pmt_t 
user_arg);
@@ -927,18 +929,19 @@
     d_got_response_recv(false)
 { 
 
-  d_nmsg_to_recv=12;
-  d_nmsg_recvd=0;
-  
   d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL);
   d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL);
 
   // Use the stub with the usrp_server
-  pmt_t usrp_server_dict = pmt_make_dict();
-  pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"), PMT_T);
+  pmt_t usrp_dict = pmt_make_dict();
+  // Set TX and RX interpolations
+  pmt_dict_set(usrp_dict,
+               pmt_intern("decim-rx"),
+               pmt_from_long(128));
+  pmt_dict_set(usrp_dict, pmt_intern("fake-usrp"), PMT_T);
 
   // Test the TX side
-  define_component("server", "usrp_server", usrp_server_dict);
+  define_component("server", "usrp_server", usrp_dict);
   connect("self", "rx0", "server", "rx0");
   connect("self", "cs", "server", "cs");
 }
@@ -967,17 +970,10 @@
              pmt_list2(PMT_NIL, 
                        pmt_from_long(0)));
 
-  // A small sleep is used to ensure, if working properly, a recv
-  // response comes through successfully before the close gets
-  // through
-  usleep(1000);
-
-  d_rx->send(s_cmd_stop_recv_raw_samples, 
-             pmt_list2(PMT_NIL, 
-                       pmt_from_long(0)));
-
-  d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T)));
-  
+  // Schedule a small timeout in which we expect to have received at least one
+  // packet worth of samples from the stub
+  d_t0 = mb_time::time();
+  schedule_one_shot_timeout(d_t0 + 0.01, PMT_NIL);
 }
 
 
@@ -992,26 +988,37 @@
   
   pmt_t expected = pmt_nth(0, data);
   pmt_t status = pmt_nth(1, data);
+
+  // If we get a timeout we shutdown
+  if(pmt_eq(event, s_timeout)) {
+    if(verbose)
+      std::cout << "[qa_rx_top] Got timeout\n";
+    d_rx->send(s_cmd_stop_recv_raw_samples, 
+               pmt_list2(PMT_NIL, 
+                         pmt_from_long(0)));
+
+    d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T)));
+    return;
+  }
   
   // For testing RX, an invocation handle is not generated by the stub,
   // therefore the same approach for testing is not used.  We simply
   // expect all responses to be true.
   if(pmt_eq(event, s_response_recv_raw_samples)) {
-    if(!pmt_eqv(status, PMT_T)) {
+    if(pmt_eqv(status, PMT_T)) {
+
       if(verbose)
-        std::cout << "Got: " << status << " Expected: " << PMT_T << "\n";
-      shutdown_all(PMT_F);
-      return;
-    }
-    else {
-      if(verbose)
         std::cout << "[qa_rx_top] Received expected response for message " 
-                  << d_nmsg_recvd
                   << " (" << event << ")\n";
 
       // All we want is 1 response receive!  Can't guarantee exact numbers
       d_got_response_recv = true;
     }
+    else {
+      if(verbose)
+        std::cout << "Got: " << status << " Expected: " << PMT_T << "\n";
+      shutdown_all(PMT_F);
+    }
     return;
   }
 
@@ -1026,8 +1033,7 @@
   } else {
     if(verbose)
       std::cout << "[qa_rx_top] Received expected response for message " 
-                << d_nmsg_recvd
-      << " (" << event << ")\n";
+                << " (" << event << ")\n";
   }
 
   if (pmt_eq(msg->port_id(), d_rx->port_symbol())) {
@@ -1051,12 +1057,7 @@
         std::cout << "[qa_rx_top] No response message before close\n";
       return;
     }
-
   }
-    
-  
-  d_nmsg_recvd++;
-
 }
 
 

Modified: 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_inband_usb_packet.cc
===================================================================
--- 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_inband_usb_packet.cc
        2007-10-29 00:44:18 UTC (rev 6727)
+++ 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_inband_usb_packet.cc
        2007-10-29 01:19:39 UTC (rev 6728)
@@ -29,6 +29,14 @@
 #include <iostream>
 #include <stdio.h>
 
+/*!
+ * \brief Aligns the packet payload on a 32 bit boundary.  This is essential to
+ * all control/status packets so that the inband FPGA code can parse them
+ * easily.
+ *
+ * \returns true if successful or if the packet was already aligned; false if 
it
+ * cannot be aligned.
+ */
 bool usrp_inband_usb_packet::align32()
 {
   int p_len = payload_len();
@@ -43,18 +51,20 @@
   if((MAX_PAYLOAD - p_len) < bytes_needed)
     return false;
     
-  p_len += bytes_needed;
+  incr_header_len(bytes_needed);
 
-  int h_flags = flags();
-  int h_chan = chan();
-  int h_tag = tag();
-  int h_payload_len = p_len;
-
-  set_header(h_flags, h_chan, h_tag, h_payload_len);
-
   return true;
 }
 
+/*!
+ * \brief Adds a ping command to the current control packet.
+ *
+ * The \p rid is the rid to be associated with the ping response and \p 
ping_val
+ * is currently unused.
+ *
+ * \returns true if adding the ping command was successful, false otherwise
+ * (i.e. no space in the current packet).
+ */
 bool usrp_inband_usb_packet::cs_ping(long rid, long ping_val)
 {
   if(!align32())
@@ -82,7 +92,15 @@
   return true;
 }
 
-
+/*!
+ * \brief Adds a ping response to the packet.  This is used by the fake USRP
+ * code to generate fake responses for pings.
+ *
+ * The \p rid is the RID to be associated with the response and \p ping_val is
+ * currently unused.
+ *
+ * \returns true if the ping reply was added successfully, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_ping_reply(long rid, long ping_val) 
 {
   if(!align32())
@@ -110,6 +128,15 @@
   return true;
 }
 
+/*!
+ * \brief Adds a write register command to the packet.
+ *
+ * The \p reg_num is the register number for which the value \p val will be
+ * written to.
+ *
+ * \returns true if the command was added to the packet successfully, false
+ * otherwise.
+ */
 bool usrp_inband_usb_packet::cs_write_reg(long reg_num, long val)
 {
   if(!align32())
@@ -143,6 +170,14 @@
   return true;
 }
 
+/*!
+ * \brief Adds a write register masked command to the packet.
+ *
+ * The \p reg_num is the register number for which the value \p val will be
+ * written, masked by \p mask
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_write_reg_masked(long reg_num, long val, long 
mask)
 {
   if(!align32())
@@ -179,6 +214,14 @@
   return true;
 }
 
+/*!
+ * \brief Adds a read register message to the packet. 
+ *
+ * The \p rid will be the associated RID returned with the response, and \p
+ * reg_num is the register to be read.
+ * 
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_read_reg(long rid, long reg_num)
 {
   if(!align32())
@@ -206,6 +249,17 @@
   return true;
 }
 
+/*!
+ * \brief Adds a read register reply response to the current packet.  This is
+ * used by the fake USRP code to generate fake register read responses for
+ * testing.
+ *
+ * The \p rid is the associated RID to be included in the response, \p reg_num
+ * is the register the read is coming from, and \p reg_val is the value of the
+ * read.
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_read_reg_reply(long rid, long reg_num, long 
reg_val)
 {
   if(!align32())
@@ -237,6 +291,14 @@
   return true;
 }
 
+/*!
+ * \brief Adds a delay command to the current packet.
+ *
+ * The \p ticks parameter is the number of clock ticks the FPGA should delay
+ * parsing for, which is added to the packet.
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_delay(long ticks)
 {
   if(!align32())
@@ -263,6 +325,11 @@
   return true;
 }
 
+/*!
+ * \brief
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_i2c_write(long i2c_addr, uint8_t *i2c_data, 
size_t data_len)
 {
   if(!align32())
@@ -297,6 +364,15 @@
   return true;
 }
 
+/*!
+ * \brief Adds an I2C read command to the current packet.
+ *
+ * The \p rid is the associated RID to return with the read response, \p
+ * i2c_addr is the address to read from on the I2C bus, and \p n_bytes is the
+ * number of bytes to be read from the bus.
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_i2c_read(long rid, long i2c_addr, long n_bytes)
 {
   if(!align32())
@@ -331,6 +407,15 @@
   return true;
 }
 
+/*!
+ * \brief Adds an I2C read reply response to the current packet.  This is used
+ * by the fake USRP code to generate fake I2C responses.
+ *
+ * The \p rid is the RID to be associated with the response, \p i2c_addr is the
+ * address on the I2C bus that the \p i2c_data of \p i2c_data_len was read 
from.
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_i2c_read_reply(long rid, long i2c_addr, 
uint8_t *i2c_data, long i2c_data_len)
 {
   if(!align32())
@@ -365,6 +450,11 @@
   return true;
 }
 
+/*!
+ * \brief Adds a SPI write command to the current packet.
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_spi_write(long enables, long format, long 
opt_header_bytes, uint8_t *spi_data, long spi_data_len)
 {
   if(!align32())
@@ -408,6 +498,11 @@
   return true;
 }
 
+/*!
+ * \brief Adds a SPI bus read command to the packet.
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_spi_read(long rid, long enables, long format, 
long opt_header_bytes, long n_bytes)
 {
   if(!align32())
@@ -457,6 +552,12 @@
   return true;
 }
 
+/*!
+ * \brief Adds an SPI read reply to the current packet.  This is used by the
+ * fake USRP code to generate fake responses for SPI reads.
+ *
+ * \returns true if the command was added to the packet, false otherwise.
+ */
 bool usrp_inband_usb_packet::cs_spi_read_reply(long rid, uint8_t *spi_data, 
long spi_data_len)
 {
   if(!align32())
@@ -490,20 +591,27 @@
   return true;
 }
 
-// Takes an offset to the beginning of a subpacket and extracts the
-// length of the subpacket
+/*!
+ * \brief Since all control packets contain subpackets which have the length of
+ * the subpacket at a uniform location in the subpacket, this will return the
+ * subpacket length given a byte offset of the start of the subpacket from the 
beginning of the packet.
+ *
+ * \returns the length of the subpacket
+ */
 int usrp_inband_usb_packet::cs_len(int payload_offset) {
   uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + 
payload_offset)));
   return (subpkt >> CS_LEN_SHIFT) & CS_LEN_MASK;
 }
 
-// The following method takes an offset within the packet payload to extract
-// a control/status subpacket and construct a pmt response which includes the
-// proper signal and arguments specified by usrp-low-level-cs.  The USRP
-// server could therefore use this to read subpackets and pass them responses
-// back up to the application.  It's arguable that only reply packets should
-// be parsed here, however we parse others for use in debugging or failure
-// reporting on the transmit side of packets.
+/*!
+ * \brief The following method takes an offset within the packet payload to
+ * extract a control/status subpacket and constructs a pmt response which
+ * includes the proper signal and arguments specified by usrp-low-level-cs.  
The
+ * USRP server could therefore use this to read subpackets and pass them
+ * responses back up to the application.  It's arguable that only reply packets
+ * should be parsed here, however we parse others for use in debugging or
+ * failure reporting on the transmit side of packets.
+ */
 pmt_t usrp_inband_usb_packet::read_subpacket(int payload_offset) {
 
   uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + 
payload_offset)));

Modified: gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx.cc
===================================================================
--- gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx.cc       
2007-10-29 00:44:18 UTC (rev 6727)
+++ gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx.cc       
2007-10-29 01:19:39 UTC (rev 6728)
@@ -73,6 +73,12 @@
   
 }
 
+/*!
+ * \brief Handles incoming signals to to the m-block, wihch should only ever be
+ * a single message: cmd-usrrp-rx-start-reading.  There is no signal to stop
+ * reading as the m-block goes in to a forever loop to read inband packets from
+ * the bus.
+ */
 void
 usrp_rx::handle_message(mb_message_sptr msg)
 {
@@ -89,6 +95,17 @@
   }
 }
 
+/*!
+ * \brief Performs the actual reading of data from the USB bus, called by
+ * handle_message() when a cmd-usrp-rx-start-reading signal is received.  
+ *
+ * The method enters a forever loop where it continues to read data from the 
bus
+ * and generate read responses to the higher layer.  Currently, shared memory 
is
+ * used to exit this loop.
+ *
+ * The \p data parameter is a PMT list which contains only a single element, an
+ * invocation handle which will be returned with all read respones.
+ */
 void
 usrp_rx::read_and_respond(pmt_t data)
 {

Modified: 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.cc
===================================================================
--- gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.cc  
2007-10-29 00:44:18 UTC (rev 6727)
+++ gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.cc  
2007-10-29 01:19:39 UTC (rev 6728)
@@ -40,7 +40,7 @@
 
 typedef usrp_inband_usb_packet transport_pkt;
 
-static const bool verbose = true;
+static const bool verbose = false;
 
 bool usrp_rx_stop_stub;
 
@@ -96,7 +96,6 @@
 void 
 usrp_rx_stub::initial_transition()
 {
-  
 }
 
 void
@@ -106,96 +105,122 @@
   pmt_t port_id = msg->port_id();
   pmt_t data = msg->data(); 
 
+  if (pmt_eq(msg->signal(), s_timeout)
+      && !pmt_eq(msg->data(), s_done)) {
+  
+    if(!usrp_rx_stop_stub) 
+      read_and_respond();
+    else {  // requested to stop
+      cancel_timeout(msg->metadata());
+      usrp_rx_stop_stub=false;
+      if(verbose)
+        std::cout << "[USRP_RX_STUB] Stopping RX stub\n";
+    }
+
+  }
+
   // Theoretically only have 1 message to ever expect, but
   // want to make sure its at least what we want
-  if(pmt_eq(port_id, d_cs->port_symbol())) {
+  if(pmt_eq(port_id, d_cs->port_symbol())
+      && pmt_eqv(event, s_cmd_usrp_rx_start_reading)) {
 
     if(verbose)
       std::cout << "[USRP_RX_STUB] Starting with decim @ " 
                 << d_decim_rx << std::endl;
     
-    if(pmt_eqv(event, s_cmd_usrp_rx_start_reading))
-      read_and_respond(data);
+      start_packet_timer();
   }
 }
 
+// Setup a periodic timer which will drive packet generation
 void
-usrp_rx_stub::read_and_respond(pmt_t data)
+usrp_rx_stub::start_packet_timer()
 {
+  d_t0 = mb_time::time();   // current time
 
-  while(!usrp_rx_stop_stub) {
+  // Calculate the inter-packet arrival time.  
+  double samples_per_sec = (64.0/(double)d_decim_rx)*1000000.0;
+  double frames_per_sec = samples_per_sec / (double)d_samples_per_frame;
+  double frame_rate = 1.0 / frames_per_sec;
 
-    long nsamples_this_frame = d_samples_per_frame;
+  if(verbose) {
+    std::cout << "[USRP_RX_STUB] Scheduling periodic packet generator\n";
+    std::cout << "\tsamples_per_sec: " << samples_per_sec << std::endl;
+    std::cout << "\tframes_per_sec: " << frames_per_sec << std::endl;
+    std::cout << "\tframe_rate: " << frame_rate << std::endl;
+  }
 
-    size_t nshorts = 2 * nsamples_this_frame;  // 16-bit I & Q
-    long channel = 0;
-    long n_bytes = nshorts*2;
-    pmt_t uvec = pmt_make_s16vector(nshorts, 0);
-    size_t ignore;
-    int16_t *samples = pmt_s16vector_writeable_elements(uvec, ignore);
+  schedule_periodic_timeout(d_t0 + frame_rate, mb_time(frame_rate), PMT_T);
+}
 
-    // fill in the complex sinusoid
+void
+usrp_rx_stub::read_and_respond()
+{
 
-    for (int i = 0; i < nsamples_this_frame; i++){
+  long nsamples_this_frame = d_samples_per_frame;
 
-      if (1){
-        gr_complex s;
-        d_nco.sincos(&s, 1, d_amplitude);
-        // write 16-bit i & q
-        samples[2*i] =   (int16_t) s.real();
-        samples[2*i+1] = (int16_t) s.imag();
-      }
-      else {
-        gr_complex s(d_amplitude, d_amplitude);
+  size_t nshorts = 2 * nsamples_this_frame;    // 16-bit I & Q
+  long channel = 0;
+  long n_bytes = nshorts*2;
+  pmt_t uvec = pmt_make_s16vector(nshorts, 0);
+  size_t ignore;
+  int16_t *samples = pmt_s16vector_writeable_elements(uvec, ignore);
 
-        // write 16-bit i & q
-        samples[2*i] =   (int16_t) s.real();
-        samples[2*i+1] = (int16_t) s.imag();
-      }
+  // fill in the complex sinusoid
+
+  for (int i = 0; i < nsamples_this_frame; i++){
+
+    if (1){
+      gr_complex s;
+      d_nco.sincos(&s, 1, d_amplitude);
+      // write 16-bit i & q
+      samples[2*i] =   (int16_t) s.real();
+      samples[2*i+1] = (int16_t) s.imag();
     }
-    
-    if(d_disk_write)
-      d_ofile.write((const char *)samples, n_bytes);
+    else {
+      gr_complex s(d_amplitude, d_amplitude);
+
+      // write 16-bit i & q
+      samples[2*i] =   (int16_t) s.real();
+      samples[2*i+1] = (int16_t) s.imag();
+    }
+  }
   
-    pmt_t v_pkt = pmt_make_u8vector(sizeof(transport_pkt), 0);
-    transport_pkt *pkt =
-      (transport_pkt *) pmt_u8vector_writeable_elements(v_pkt, ignore);
+  if(d_disk_write)
+    d_ofile.write((const char *)samples, n_bytes);
 
-    pkt->set_header(0, channel, 0, n_bytes);
-    pkt->set_timestamp(0xffffffff);
-    memcpy(pkt->payload(), samples, n_bytes);
-    
-    d_cs->send(s_response_usrp_rx_read, pmt_list3(PMT_NIL, PMT_T, v_pkt));
+  pmt_t v_pkt = pmt_make_u8vector(sizeof(transport_pkt), 0);
+  transport_pkt *pkt =
+    (transport_pkt *) pmt_u8vector_writeable_elements(v_pkt, ignore);
 
-    // Now lets check the shared CS queue between the TX and RX stub.  Each
-    // element in a queue is a list where the first element is an invocation
-    // handle and the second element is a PMT u8 vect representation of the
-    // CS packet response which can just be passed transparently.
-    while(!d_cs_queue.empty()) {
-      
-      pmt_t cs_pkt = d_cs_queue.front();
-      d_cs_queue.pop();
+  pkt->set_header(0, channel, 0, n_bytes);
+  pkt->set_timestamp(0xffffffff);
+  memcpy(pkt->payload(), samples, n_bytes);
+  
+  d_cs->send(s_response_usrp_rx_read, pmt_list3(PMT_NIL, PMT_T, v_pkt));
 
-      pmt_t invocation_handle = pmt_nth(0, cs_pkt);
-      pmt_t v_pkt = pmt_nth(1, cs_pkt);
+  // Now lets check the shared CS queue between the TX and RX stub.  Each
+  // element in a queue is a list where the first element is an invocation
+  // handle and the second element is a PMT u8 vect representation of the
+  // CS packet response which can just be passed transparently.
+  while(!d_cs_queue.empty()) {
+    
+    pmt_t cs_pkt = d_cs_queue.front();
+    d_cs_queue.pop();
 
-      d_cs->send(s_response_usrp_rx_read,   
-                 pmt_list3(invocation_handle, 
-                           PMT_T, 
-                           v_pkt));  // Take the front CS pkt
+    pmt_t invocation_handle = pmt_nth(0, cs_pkt);
+    pmt_t v_pkt = pmt_nth(1, cs_pkt);
 
-      
-      if(verbose)
-        std::cout << "[USRP_RX_STUB] Received CS response from TX stub\n";
-    }
+    d_cs->send(s_response_usrp_rx_read,   
+               pmt_list3(invocation_handle, 
+                         PMT_T, 
+                         v_pkt));  // Take the front CS pkt
 
+    
+    if(verbose)
+      std::cout << "[USRP_RX_STUB] Received CS response from TX stub\n";
   }
-  
-  usrp_rx_stop_stub = false;
 
-  if(verbose)
-    std::cout << "[USRP_RX_STUB] Got fake RX stop\n";
-
 }
 
 REGISTER_MBLOCK_CLASS(usrp_rx_stub);

Modified: 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.h
===================================================================
--- gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.h   
2007-10-29 00:44:18 UTC (rev 6727)
+++ gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_rx_stub.h   
2007-10-29 01:19:39 UTC (rev 6728)
@@ -34,6 +34,9 @@
 extern bool usrp_rx_stop_stub;   // used to communicate a 'stop' to the RX stub
 extern std::queue<pmt_t> d_cs_queue;
 
+static pmt_t s_timeout = pmt_intern("%timeout");
+static pmt_t s_done = pmt_intern("done");
+
 /*!
  * \brief Implements the low level usb interface to the USRP
  */
@@ -46,6 +49,9 @@
   
   long         d_samples_per_frame;
   long    d_decim_rx;
+
+  mb_time d_t0;
+  double d_delta_t;
   
   // for generating sine wave output
   ui_nco<float,float>  d_nco;
@@ -62,8 +68,9 @@
   void handle_message(mb_message_sptr msg);
 
  private:
-  void read_and_respond(pmt_t data);
+  void read_and_respond();
   void read_data();
+  void start_packet_timer();
  
 };
   

Modified: 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_server.cc
===================================================================
--- gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_server.cc   
2007-10-29 00:44:18 UTC (rev 6727)
+++ gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_server.cc   
2007-10-29 01:19:39 UTC (rev 6728)
@@ -41,7 +41,7 @@
 
 typedef usrp_inband_usb_packet transport_pkt;   // makes conversion to gigabit 
easy
 
-const static bool verbose = true;
+const static bool verbose = false;
 
 static std::string
 str(long x)
@@ -137,6 +137,10 @@
   //d_fake_rx=true;
 }
 
+/*!
+ * \brief resets the assigned capacity and owners of each RX and TX channel 
from
+ * allocations.
+ */
 void
 usrp_server::reset_channels()
 {
@@ -165,6 +169,11 @@
   // the initial transition
 }
 
+/*!
+ * \brief Reads all incoming messages to USRP server from the TX, RX, and the 
CS
+ * ports.  This drives the state of USRP server and dispatches based on the
+ * message.
+ */
 void
 usrp_server::handle_message(mb_message_sptr msg)
 {
@@ -502,7 +511,14 @@
   std::cout << "[USRP_SERVER] unhandled msg: " << msg << std::endl;
 }
 
-// Return -1 if it is not an RX port, or an index
+/*!
+ * \brief Takes a port_symbol() as parameter \p port_id and is used to 
determine
+ * if the port is a TX port, or to find an index in the d_tx vector which 
stores
+ * the port.
+ *
+ * \returns -1 if \p port_id is not in the d_tx vector (i.e., it's not a TX
+ * port), otherwise returns an index in the d_tx vector which stores the port.
+ */
 int usrp_server::tx_port_index(pmt_t port_id) {
 
   for(int i=0; i < (int) d_tx.size(); i++) 
@@ -512,7 +528,14 @@
   return -1;
 }
 
-// Return -1 if it is not an RX port, or an index
+/*!
+ * \brief Takes a port_symbol() as parameter \p port_id and is used to 
determine
+ * if the port is an RX port, or to find an index in the d_rx vector which
+ * stores the port.
+ *
+ * \returns -1 if \p port_id is not in the d_rx vector (i.e., it's not an RX
+ * port), otherwise returns an index in the d_rx vector which stores the port.
+ */
 int usrp_server::rx_port_index(pmt_t port_id) {
   
   for(int i=0; i < (int) d_rx.size(); i++) 
@@ -522,8 +545,12 @@
   return -1;
 }
 
-// Go through all TX and RX channels, sum up the assigned capacity
-// and return it
+/*!
+ * \brief Determines the current total capacity allocated by all RX and TX
+ * channels.
+ *
+ * \returns the total allocated capacity
+ */
 long usrp_server::current_capacity_allocation() {
   long capacity = 0;
 
@@ -536,6 +563,14 @@
   return capacity;
 }
     
+
+/*!
+ * \brief Called by the handle_message() method if the incoming message to
+ * usrp_server is to allocate a channel (cmd-allocate-channel).  The method
+ * checks if the requested capacity exists and if so it will reserve it for the
+ * caller on the channel that is returned via a response-allocate-channel
+ * signal.
+ */
 void 
 usrp_server::handle_cmd_allocate_channel(
                                 mb_port_sptr port, 
@@ -596,9 +631,13 @@
   return;
 }
 
-// Check the port type and deallocate assigned capacity based on this, ensuring
-// that the owner of the method invocation is the owner of the port and that 
the
-// channel number is valid.
+/*!
+ * \brief Called by the handle_message() method if the incoming message to
+ * usrp_server is to deallocate a channel (cmd-deallocate-channel).  The method
+ * ensures that the sender of the signal owns the channel and that the channel
+ * number is valid.  A response-deallocate-channel signal is sent back with the
+ * result of the deallocation.
+ */
 void 
 usrp_server::handle_cmd_deallocate_channel(
                               mb_port_sptr port, 
@@ -623,8 +662,26 @@
   return;
 }
 
-void usrp_server::handle_cmd_xmit_raw_frame(mb_port_sptr port, 
std::vector<struct channel_info> &chan_info, pmt_t data) {
-
+/*!
+ * \brief Called by the handle_message() method if the incoming message to
+ * usrp_server is to transmit a frame (cmd-xmit-raw-frame).  The method
+ * allocates enough memory to support a burst of packets which contain the 
frame
+ * over the bus of the frame, sets the packet headers, and sends a signal to 
the
+ * lower block for the data (packets) to be written to the bus.  
+ *
+ * The \p port the command was sent on and the channel info (\p chan_info) of
+ * the channel the frame is to be transmitted on are passed to ensure that the
+ * caller owns the channel.
+ *
+ * The \p data parameter is in the format of a cmd-xmit-raw-frame signal.
+ *
+ * The properties
+ */
+void usrp_server::handle_cmd_xmit_raw_frame(
+                              mb_port_sptr port, 
+                              std::vector<struct channel_info> &chan_info, 
+                              pmt_t data) 
+{
   size_t n_bytes, psize;
   long max_payload_len = transport_pkt::max_payload();
 
@@ -699,7 +756,8 @@
               << invocation_handle << std::endl;
     
   // The actual response to the write will be generated by a
-  // s_response_usrp_write
+  // s_response_usrp_write since we cannot determine whether to transmit was
+  // successful until we hear from the lower layers.
   d_cs_usrp->send(s_cmd_usrp_write, 
                   pmt_list3(invocation_handle, 
                             pmt_from_long(channel), 
@@ -708,7 +766,29 @@
   return;
 }
 
-void usrp_server::handle_cmd_to_control_channel(mb_port_sptr port, 
std::vector<struct channel_info> &chan_info, pmt_t data) 
+/*!
+ * \brief Called by the handle_message() method to parse incoming 
control/status
+ * signals (cmd-to-control-channel).  
+ * 
+ * The \p port the command was sent on and the channel info (\p chan_info) of
+ * the channel are passed to ensure that the caller owns the channel.
+ *
+ * The \p data parameter is in the format of a PMT list, where each element
+ * follows the format of a control/status signal (i.e. op-ping-fixed).
+ *
+ * The method will parse all of the C/S commands included in \p data and place
+ * the commands in to a lower level packet sent to the control channel.  The
+ * method will pack as many commands as possible in t oa single packet, and 
once
+ * it is fill generate as many lower level packets as needed.
+ *
+ * Anything that needs to be returned to the sender of the signal (i.e. the
+ * value of a register) will be generated by the parse_control_pkt() method as
+ * the responses to the commands are read back from the USRP.
+ */
+void usrp_server::handle_cmd_to_control_channel(
+                            mb_port_sptr port, 
+                            std::vector<struct channel_info> &chan_info, 
+                            pmt_t data) 
 {
 
   pmt_t invocation_handle = pmt_nth(0, data);
@@ -1022,8 +1102,22 @@
   return;
 }
 
+/*!
+ * \brief Called by the handle_message() method when the incoming signal is a
+ * command to start reading samples from the USRP 
(cmd-start-recv-raw-samples).  
+ *
+ * The \p port the command was sent on and the channel info (\p chan_info) of
+ * the channel are passed to ensure that the caller owns the channel.
+ *
+ * The \p data parameter should be in the format of a 
cmd-start-recv-raw-samples
+ * command where the first element in the list is an invocation handle, and the
+ * second is the channel the signal generator wants to receive the samples on.
+ */
 void
-usrp_server::handle_cmd_start_recv_raw_samples(mb_port_sptr port, 
std::vector<struct channel_info> &chan_info, pmt_t data)
+usrp_server::handle_cmd_start_recv_raw_samples(
+                                  mb_port_sptr port, 
+                                  std::vector<struct channel_info> &chan_info, 
+                                  pmt_t data)
 {
   pmt_t invocation_handle = pmt_nth(0, data);
   long channel = pmt_to_long(pmt_nth(1, data));
@@ -1064,6 +1158,18 @@
   return;
 }
 
+/*!
+ * \brief Called by the handle_message() method when the incoming signal is to
+ * stop receiving samples from the USRP (cmd-stop-recv-raw-samples).
+ *
+ * The \p port the command was sent on and the channel info (\p chan_info) of
+ * the channel are passed to ensure that the caller owns the channel.
+ *
+ * The \p data parameter should be in the format of a cmd-stop-recv-raw-samples
+ * command where the first element in the list is an invocation handle, and the
+ * second is the channel the signal generator wants to stop receiving the
+ * samples from.
+ */
 void
 usrp_server::handle_cmd_stop_recv_raw_samples(
                         mb_port_sptr port, 
@@ -1099,7 +1205,22 @@
   return;
 }
 
-// Read the packet header, determine the port by the channel owner
+/*!
+ * \brief Called by the handle_message() method when an incoming signal is
+ * generated to USRP server that contains raw samples from the USRP.  This
+ * method generates the response-recv-raw-samples signals that are the result 
of
+ * a cmd-start-recv-raw-samples signal.
+ *
+ * The raw lower-level packet is extracted from \p data, where the format for 
\p
+ * data is a PMT list.  The PMT \p data list should contain an invocation 
handle
+ * as the first element, the status of the lower-level read as the second
+ * element, and a uniform vector representation of the packets as the third
+ * element.  
+ *
+ * The packet contains a channel field that the samples are destined to, and 
the
+ * method determines where to send the samples based on this channel since each
+ * channel has an associated port which allocated it.
+ */
 void
 usrp_server::handle_response_usrp_read(pmt_t data)
 {
@@ -1177,6 +1298,19 @@
   return;
 }
 
+/*!
+ * \brief Called by handle_response_usrp_read() when the incoming packet has a
+ * channel of CONTROL_CHAN.  This means that the incoming packet contains a
+ * response for a command sent to the control channel, which this method will
+ * parse.
+ *
+ * The \p pkt parameter is a pointer to the full packet (transport_pkt) in
+ * memory.
+ *
+ * Given that all commands sent to the control channel that require responses
+ * will carry an RID (request ID), the method will use the RID passed back with
+ * the response to determine which port the response should be sent on.
+ */
 void
 usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt)
 {
@@ -1222,6 +1356,9 @@
         return;
 
       pmt_t owner = d_rids[srid].owner;
+      
+      // Return the RID
+      d_rids[srid].owner = PMT_NIL;
 
       // FIXME: should be 1 response for all subpackets here ?
       if((port = tx_port_index(owner)) != -1)
@@ -1257,6 +1394,9 @@
         return;
       
       pmt_t owner = d_rids[srid].owner;
+      
+      // Return the RID
+      d_rids[srid].owner = PMT_NIL;
 
       // FIXME: should be 1 response for all subpackets here ?
       if((port = tx_port_index(owner)) != -1)
@@ -1293,6 +1433,9 @@
         return;
 
       pmt_t owner = d_rids[srid].owner;
+      
+      // Return the RID
+      d_rids[srid].owner = PMT_NIL;
 
       if((port = tx_port_index(owner)) != -1)
         d_tx[port]->send(s_response_from_control_channel,
@@ -1326,6 +1469,9 @@
         return;
 
       pmt_t owner = d_rids[srid].owner;
+      
+      // Return the RID
+      d_rids[srid].owner = PMT_NIL;
 
       if((port = tx_port_index(owner)) != -1)
         d_tx[port]->send(s_response_from_control_channel,
@@ -1349,6 +1495,10 @@
   }
 }
 
+/*!
+ * \brief Used to recall all incoming signals that were deferred when USRP
+ * server was in the initialization state.
+ */
 void
 usrp_server::recall_defer_queue()
 {
@@ -1367,6 +1517,25 @@
   return;
 }
 
+/*!
+ * \brief Commonly called by any method which handles outgoing frames or 
control
+ * packets to the USRP to check if the port on which the signal was sent owns
+ * the channel the outgoing packet will be associated with.   This helps ensure
+ * that applications do not send data on other application's ports.
+ *
+ * The \p port parameter is the port symbol that the caller wishes to determine
+ * owns the channel specified by \p chan_info.  
+ *
+ * The \p signal_info parameter is a PMT list containing two elements: the
+ * response signal to use if the permissions are invalid, and the invocation
+ * handle that was passed.  This allows the method to generate detailed failure
+ * responses to signals without having to return some sort of structured
+ * information which the caller must then parse and interpret to determine the
+ * failure type.
+ *
+ * \returns true if \p port owns the channel specified by \p chan_info, false
+ * otherwise.
+ */
 bool
 usrp_server::check_valid(mb_port_sptr port,
                          long channel,
@@ -1409,8 +1578,12 @@
   return true;
 }
 
-// Goes through the vector of RIDs and retreieves an
-// available one for use
+/*!
+ * \brief Finds the next available RID for internal USRP server use with 
control
+ * and status packets.
+ *
+ * \returns the next valid RID or -1 if no more RIDs are available.
+ */
 long
 usrp_server::next_rid()
 {
@@ -1418,14 +1591,16 @@
     if(pmt_eqv(d_rids[i].owner, PMT_NIL))
       return i;
 
+  if(verbose)
+    std::cout << "[USRP_SERVER] No RIDs left\n";
   return -1;
 }
 
-// Need to initialize several registers according to the specified settings of
-// the user
-//
-// FIXME: I believe this is specific to the basic daughterboards, this would
-// need to change when daughterboard support is added
+/*!
+ * \brief Called by handle_message() when USRP server gets a response that the
+ * USRP was opened successfully to initialize the registers using the new
+ * register read/write control packets.
+ */
 void
 usrp_server::initialize_registers()
 {
@@ -1513,6 +1688,13 @@
   set_register(FR_RX_FREQ_2, 0);
   set_register(FR_RX_FREQ_3, 0);
 
+  // Enable debug bus
+  set_register(FR_DEBUG_EN, 0xf);
+  set_register(FR_OE_0, -1);
+  set_register(FR_OE_1, -1);
+  set_register(FR_OE_2, -1);
+  set_register(FR_OE_3, -1);
+
   // DEBUGGING
   //check_register_initialization();
 }
@@ -1601,6 +1783,10 @@
   read_register(FR_RX_FREQ_3);
 }
 
+/*!
+ * \brief Used to generate FPGA register write commands to reset all of the 
FPGA
+ * registers to a value of 0.
+ */
 void
 usrp_server::reset_all_registers()
 {
@@ -1608,15 +1794,13 @@
     set_register(i, 0);
 }
 
-// THIS IS ONLY FOR INTERNAL USRP_SERVER USAGE
-//
-// This function needs to create its own USB packet and not use
-// handle_cmd_to_control_channel because in many cases such as initialization,
-// the number of register writes needed are more than the number of RID's.  If
-// there are no RID's left for use then handle_cmd_to_control_channel() will
-// throw away the request.  This allows usrp_server to generate its own 
register
-// writes without eating up RID's, since usrp_server does not really care about
-// the response.
+/*!
+ * \brief Used internally by USRP server to generate a control/status packet
+ * which contains a register write.
+ *
+ * The \p reg parameter is the register number that the value \p val will be
+ * written to.
+ */
 void
 usrp_server::set_register(long reg, long val)
 {
@@ -1637,16 +1821,34 @@
                             v_packet));
 }
 
-// Only for internal usrp_server usage.  Structed the same way as
-// set_register().
+/*!
+ * \brief Used internally by USRP server to generate a control/status packet
+ * which contains a register read.  This is important to use internally so that
+ * USRP server can bypass the use of RIDs with register reads, as they are not
+ * needed and it would use up the finite number of RIDs available for use for
+ * applications to receive responses.
+ *
+ * The \p reg parameter is the register number that the value should be read
+ * from.
+ */
 void
 usrp_server::read_register(long reg)
 {
-  handle_cmd_to_control_channel(d_tx[0], d_chaninfo_tx,
-    pmt_list2(PMT_NIL,  // empty invoc handle
-              pmt_list1(pmt_list2(s_op_read_reg,
-                        pmt_list2(pmt_from_long(0),
-                                  pmt_from_long(reg))))));
+  size_t psize;
+  long payload_len = 0;
+
+  pmt_t v_packet = pmt_make_u8vector(sizeof(transport_pkt), 0);
+  transport_pkt *pkt = (transport_pkt *) 
pmt_u8vector_writeable_elements(v_packet, psize);
+  
+  pkt->set_header(0, CONTROL_CHAN, 0, payload_len);
+  pkt->set_timestamp(0xffffffff);
+
+  pkt->cs_read_reg(0, reg);
+
+  d_cs_usrp->send(s_cmd_usrp_write, 
+                  pmt_list3(PMT_NIL, 
+                            pmt_from_long(CONTROL_CHAN), 
+                            v_packet));
 }
 
 REGISTER_MBLOCK_CLASS(usrp_server);

Modified: gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_tx.cc
===================================================================
--- gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_tx.cc       
2007-10-29 00:44:18 UTC (rev 6727)
+++ gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_tx.cc       
2007-10-29 01:19:39 UTC (rev 6728)
@@ -66,6 +66,10 @@
   
 }
 
+/*!
+ * \brief Handles incoming signals to to the m-block, wihch should only ever be
+ * a single message: cmd-usrp-tx-write.
+ */
 void
 usrp_tx::handle_message(mb_message_sptr msg)
 {
@@ -82,6 +86,14 @@
   }
 }
 
+/*!
+ * \brief Performs the actual writing of data to the USB bus, called by
+ * handle_message() when a cmd-usrp-tx-write signal is received.
+ *
+ * The \p data parameter is a PMT list which contains three mandatory elements,
+ * in the following order: an invocation handle, a channel, and a uniform 
vector
+ * of memory which contains the packets to be written to the bus.
+ */
 void
 usrp_tx::write(pmt_t data)
 {

Modified: 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_usb_interface.cc
===================================================================
--- 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_usb_interface.cc
    2007-10-29 00:44:18 UTC (rev 6727)
+++ 
gnuradio/branches/features/inband-usb/usrp/host/lib/inband/usrp_usb_interface.cc
    2007-10-29 01:19:39 UTC (rev 6728)
@@ -47,8 +47,12 @@
 static const bool verbose = false;
 
 
-
-// need to take number of TX and RX channels as parameter
+/*!
+ * \brief Initializes the USB interface m-block.
+ *
+ * The \p user_arg should be a PMT dictionary which can contain optional
+ * arguments for the block, such as the decimatoin and interpolation rate.
+ */
 usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string 
&instance_name, pmt_t user_arg)
   : mb_mblock(rt, instance_name, user_arg),
   d_fake_usrp(false),
@@ -159,6 +163,10 @@
 
 }
 
+/*!
+ * \brief Handles all incoming signals to the block from the lowest m-blocks
+ * which read/write to the bus, or the higher m-block which is the USRP server.
+ */
 void
 usrp_usb_interface::handle_message(mb_message_sptr msg)
 {
@@ -254,6 +262,13 @@
   std::cout << "[USRP_USB_INTERFACE] unhandled msg: " << msg << std::endl;
 }
 
+/*!
+ * \brief Called by the handle_message() method when the incoming signal is to
+ * open a USB connection to the USRP (cmd-usrp-open).
+ *
+ * The \p data parameter is a PMT list, where the elements are an invocation
+ * handle and the USRP number.
+ */
 void
 usrp_usb_interface::handle_cmd_open(pmt_t data)
 {
@@ -339,6 +354,14 @@
   d_cs->send(s_response_usrp_open, pmt_list2(invocation_handle, PMT_T));
 }
 
+/*!
+ * \brief Called by the handle_message() method when the incoming signal is to
+ * write data to the USB bus (cmd-usrp-write). 
+ *
+ * The \p data parameter is a PMT list containing 3 mandatory elements in the
+ * following order: an invocation handle, channel, and a uniform vector
+ * representation of the packets.
+ */
 void
 usrp_usb_interface::handle_cmd_write(pmt_t data)
 {
@@ -357,6 +380,13 @@
   return;
 }
 
+/*!
+ * \brief Called by the handle_message() method when the incoming signal is to
+ * start reading data from the USB bus (cmd-usrp-start-reading).
+ *
+ * The \p data parameter is a PMT list with a single element: an invocation
+ * handle which can be returned with the response.
+ */
 void
 usrp_usb_interface::handle_cmd_start_reading(pmt_t data)
 {
@@ -377,6 +407,13 @@
   return;
 }
 
+/*!
+ * \brief Called by the handle_message() method when the incoming signal is to
+ * stop reading data from the USB bus (cmd-usrp-stop-reading).
+ *
+ * The \p data parameter is a PMT list with a single element: an invocation
+ * handle which can be returned with the response.
+ */
 void
 usrp_usb_interface::handle_cmd_stop_reading(pmt_t data)
 {
@@ -386,6 +423,10 @@
     if(verbose)
       std::cout << "[USRP_USB_INTERFACE] Stopping RX...\n";
     usrp_rx_stop = true;
+
+    // Used to allow a read() being called by a lower layer to complete before
+    // stopping, else there can be partial data left on the bus and can 
generate
+    // errors.
     while(usrp_rx_stop) {usleep(1);}
     d_urx->stop();
   }
@@ -400,6 +441,13 @@
   return;
 }
 
+/*!
+ * \brief Called by the handle_message() method when the incoming signal is to
+ * close the USB connection to the USRP.
+ *
+ * The \p data parameter is a PMT list with a single element: an invocation
+ * handle which can be returned with the response.
+ */
 void
 usrp_usb_interface::handle_cmd_close(pmt_t data)
 {





reply via email to

[Prev in Thread] Current Thread [Next in Thread]