Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_flat_flowgraph.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007 Free Software Foundation, Inc.
4  *
5  * This file is part of GNU Radio
6  *
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  *
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <gr_flat_flowgraph.h>
28 #include <gr_block_detail.h>
29 #include <gr_io_signature.h>
30 #include <gr_buffer.h>
31 #include <iostream>
32 #include <map>
33
34 #define GR_FLAT_FLOWGRAPH_DEBUG 0
35
36 // 32Kbyte buffer size between blocks
37 #define GR_FIXED_BUFFER_SIZE (32*(1L<<10))
38
39 static const unsigned int s_fixed_buffer_size = GR_FIXED_BUFFER_SIZE;
40
41 gr_flat_flowgraph_sptr
42 gr_make_flat_flowgraph()
43 {
44   return gr_flat_flowgraph_sptr(new gr_flat_flowgraph());
45 }
46
47 gr_flat_flowgraph::gr_flat_flowgraph()
48 {
49 }
50
51 gr_flat_flowgraph::~gr_flat_flowgraph()
52 {
53 }
54
55 void
56 gr_flat_flowgraph::setup_connections()
57 {
58   gr_basic_block_vector_t blocks = calc_used_blocks();
59
60   // Assign block details to blocks
61   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
62     cast_to_block_sptr(*p)->set_detail(allocate_block_detail(*p));
63
64   // Connect inputs to outputs for each block
65   for(gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
66     connect_block_inputs(*p);
67 }
68
69 gr_block_detail_sptr
70 gr_flat_flowgraph::allocate_block_detail(gr_basic_block_sptr block)
71 {
72   int ninputs = calc_used_ports(block, true).size();
73   int noutputs = calc_used_ports(block, false).size();
74   gr_block_detail_sptr detail = gr_make_block_detail(ninputs, noutputs);
75
76   if (GR_FLAT_FLOWGRAPH_DEBUG)
77     std::cout << "Creating block detail for " << block << std::endl;
78
79   for (int i = 0; i < noutputs; i++) {
80     gr_buffer_sptr buffer = allocate_buffer(block, i);
81     if (GR_FLAT_FLOWGRAPH_DEBUG)
82       std::cout << "Allocated buffer for output " << block << ":" << i << std::endl;
83     detail->set_output(i, buffer);
84   }
85
86   return detail;
87 }
88
89 gr_buffer_sptr
90 gr_flat_flowgraph::allocate_buffer(gr_basic_block_sptr block, int port)
91 {
92   gr_block_sptr grblock = cast_to_block_sptr(block);
93   if (!grblock)
94     throw std::runtime_error("allocate_buffer found non-gr_block");
95   int item_size = block->output_signature()->sizeof_stream_item(port);
96
97   // *2 because we're now only filling them 1/2 way in order to
98   // increase the available parallelism when using the TPB scheduler.
99   // (We're double buffering, where we used to single buffer)
100   int nitems = s_fixed_buffer_size * 2 / item_size;
101
102   // Make sure there are at least twice the output_multiple no. of items
103   if (nitems < 2*grblock->output_multiple())    // Note: this means output_multiple()
104     nitems = 2*grblock->output_multiple();      // can't be changed by block dynamically
105
106   // If any downstream blocks are decimators and/or have a large output_multiple,
107   // ensure we have a buffer at least twice their decimation factor*output_multiple
108   gr_basic_block_vector_t blocks = calc_downstream_blocks(block, port);
109
110   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
111     gr_block_sptr dgrblock = cast_to_block_sptr(*p);
112     if (!dgrblock)
113       throw std::runtime_error("allocate_buffer found non-gr_block");
114
115     double decimation = (1.0/dgrblock->relative_rate());
116     int multiple      = dgrblock->output_multiple();
117     int history       = dgrblock->history();
118     nitems = std::max(nitems, static_cast<int>(2*(decimation*multiple+history)));
119   }
120
121   return gr_make_buffer(nitems, item_size, grblock);
122 }
123
124 void
125 gr_flat_flowgraph::connect_block_inputs(gr_basic_block_sptr block)
126 {
127   gr_block_sptr grblock = cast_to_block_sptr(block);
128   if (!grblock)
129     throw std::runtime_error("connect_block_inputs found non-gr_block");
130   
131   // Get its detail and edges that feed into it
132   gr_block_detail_sptr detail = grblock->detail();
133   gr_edge_vector_t in_edges = calc_upstream_edges(block);
134   
135   // For each edge that feeds into it
136   for (gr_edge_viter_t e = in_edges.begin(); e != in_edges.end(); e++) {
137     // Set the buffer reader on the destination port to the output
138     // buffer on the source port
139     int dst_port = e->dst().port();
140     int src_port = e->src().port();
141     gr_basic_block_sptr src_block = e->src().block();
142     gr_block_sptr src_grblock = cast_to_block_sptr(src_block);
143     if (!src_grblock)
144       throw std::runtime_error("connect_block_inputs found non-gr_block");
145     gr_buffer_sptr src_buffer = src_grblock->detail()->output(src_port);
146     
147     if (GR_FLAT_FLOWGRAPH_DEBUG)
148       std::cout << "Setting input " << dst_port << " from edge " << (*e) << std::endl;
149
150     detail->set_input(dst_port, gr_buffer_add_reader(src_buffer, grblock->history()-1, grblock));
151   }
152 }
153
154 void
155 gr_flat_flowgraph::merge_connections(gr_flat_flowgraph_sptr old_ffg)
156 {
157   // Allocate block details if needed.  Only new blocks that aren't pruned out
158   // by flattening will need one; existing blocks still in the new flowgraph will
159   // already have one.
160   for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
161     gr_block_sptr block = cast_to_block_sptr(*p);
162     
163     if (!block->detail()) {
164       if (GR_FLAT_FLOWGRAPH_DEBUG)
165         std::cout << "merge: allocating new detail for block " << (*p) << std::endl;
166         block->set_detail(allocate_block_detail(block));
167     }
168     else
169       if (GR_FLAT_FLOWGRAPH_DEBUG)
170         std::cout << "merge: reusing original detail for block " << (*p) << std::endl;
171   }
172
173   // Calculate the old edges that will be going away, and clear the buffer readers
174   // on the RHS.
175   for (gr_edge_viter_t old_edge = old_ffg->d_edges.begin(); old_edge != old_ffg->d_edges.end(); old_edge++) {
176     if (GR_FLAT_FLOWGRAPH_DEBUG)
177       std::cout << "merge: testing old edge " << (*old_edge) << "...";
178       
179     gr_edge_viter_t new_edge;
180     for (new_edge = d_edges.begin(); new_edge != d_edges.end(); new_edge++)
181       if (new_edge->src() == old_edge->src() &&
182           new_edge->dst() == old_edge->dst())
183         break;
184
185     if (new_edge == d_edges.end()) { // not found in new edge list
186       if (GR_FLAT_FLOWGRAPH_DEBUG)
187         std::cout << "not in new edge list" << std::endl;
188       // zero the buffer reader on RHS of old edge
189       gr_block_sptr block(cast_to_block_sptr(old_edge->dst().block()));
190       int port = old_edge->dst().port();
191       block->detail()->set_input(port, gr_buffer_reader_sptr());
192     }
193     else {
194       if (GR_FLAT_FLOWGRAPH_DEBUG)
195         std::cout << "found in new edge list" << std::endl;
196     }
197   }  
198
199   // Now connect inputs to outputs, reusing old buffer readers if they exist
200   for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
201     gr_block_sptr block = cast_to_block_sptr(*p);
202
203     if (GR_FLAT_FLOWGRAPH_DEBUG)
204       std::cout << "merge: merging " << (*p) << "...";
205     
206     if (old_ffg->has_block_p(*p)) {
207       // Block exists in old flow graph
208       if (GR_FLAT_FLOWGRAPH_DEBUG)
209         std::cout << "used in old flow graph" << std::endl;
210       gr_block_detail_sptr detail = block->detail();
211         
212       // Iterate through the inputs and see what needs to be done
213       int ninputs = calc_used_ports(block, true).size(); // Might be different now
214       for (int i = 0; i < ninputs; i++) {
215         if (GR_FLAT_FLOWGRAPH_DEBUG)
216           std::cout << "Checking input " << block << ":" << i << "...";
217         gr_edge edge = calc_upstream_edge(*p, i);
218
219         // Fish out old buffer reader and see if it matches correct buffer from edge list
220         gr_block_sptr src_block = cast_to_block_sptr(edge.src().block());
221         gr_block_detail_sptr src_detail = src_block->detail();
222         gr_buffer_sptr src_buffer = src_detail->output(edge.src().port());
223         gr_buffer_reader_sptr old_reader;
224         if (i < detail->ninputs()) // Don't exceed what the original detail has 
225           old_reader = detail->input(i);
226         
227         // If there's a match, use it
228         if (old_reader && (src_buffer == old_reader->buffer())) {
229           if (GR_FLAT_FLOWGRAPH_DEBUG)
230             std::cout << "matched, reusing" << std::endl;
231         }
232         else {
233           if (GR_FLAT_FLOWGRAPH_DEBUG)
234             std::cout << "needs a new reader" << std::endl;
235
236           // Create new buffer reader and assign
237           detail->set_input(i, gr_buffer_add_reader(src_buffer, block->history()-1, block));
238         }
239       }
240     }
241     else {
242       // Block is new, it just needs buffer readers at this point
243       if (GR_FLAT_FLOWGRAPH_DEBUG)
244         std::cout << "new block" << std::endl;
245       connect_block_inputs(block);
246     }
247
248     // Now deal with the fact that the block details might have changed numbers of 
249     // inputs and outputs vs. in the old flowgraph.
250   }  
251 }
252
253 void gr_flat_flowgraph::dump()
254 {
255   for (gr_edge_viter_t e = d_edges.begin(); e != d_edges.end(); e++)
256      std::cout << " edge: " << (*e) << std::endl;
257
258   for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
259     std::cout << " block: " << (*p) << std::endl;
260     gr_block_detail_sptr detail = cast_to_block_sptr(*p)->detail();
261     std::cout << "  detail @" << detail << ":" << std::endl;
262      
263     int ni = detail->ninputs();
264     int no = detail->noutputs();
265     for (int i = 0; i < no; i++) {
266       gr_buffer_sptr buffer = detail->output(i);
267       std::cout << "   output " << i << ": " << buffer 
268                 << " space=" << buffer->space_available() << std::endl;
269     }
270
271     for (int i = 0; i < ni; i++) {
272       gr_buffer_reader_sptr reader = detail->input(i);
273       std::cout << "   reader " <<  i << ": " << reader
274                 << " reading from buffer=" << reader->buffer()
275                 << " avail=" << reader->items_available() << " items"
276                 << std::endl;
277     }
278   }
279
280 }
281
282 gr_block_vector_t
283 gr_flat_flowgraph::make_block_vector(gr_basic_block_vector_t &blocks)
284 {
285   gr_block_vector_t result;
286   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
287     result.push_back(cast_to_block_sptr(*p));
288   }
289
290   return result;
291 }