CGRA-ME
ModuleFuncUnit.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * The software programs comprising "CGRA-ME" and the documentation provided
3  * with them are copyright by its authors and the University of Toronto. Only
4  * non-commercial, not-for-profit use of this software is permitted without ex-
5  * plicit permission. This software is provided "as is" with no warranties or
6  * guarantees of support. See the LICENCE for more details. You should have re-
7  * ceived a copy of the full licence along with this software. If not, see
8  * <http://cgra-me.ece.utoronto.ca/license/>.
9  ******************************************************************************/
10 
11 #include <CGRA/Module.h>
12 
13 #include <regex>
14 
15 /************ FuncUnit **********/
16 // Map that stores all possible modes of a FuncUnit
17 const std::map<OpGraphOpCode, LLVMMode> FuncUnit::all_modes =
18 {
19  // Adding functionality to the funcunit
20  // You can add a mode to the FuncUnit by specifying all_modes.push_back ({"module name", {"functionality"}, "wire name that feeds into the multiplexer"});
21  // Functionality can be multiple lines, hence the reason why it is a vector. Each string represents one line, hence the reason it is a vector
22  {OpCode::NOP, {"op_nop", "nop", {"assign c = a;"}, "nop_sel"}},
23  {OpCode::ADD, {"op_add", "add", {"assign c = a + b ;"}, "add_sel"}},
24  {OpCode::SUB, {"op_sub", "sub", {"assign c = a - b;"}, "sub_sel"}},
25  {OpCode::MUL, {"op_multiply", "multiply", {"assign c = a * b;"}, "mul_sel"}},
26  {OpCode::DIV, {"op_divide", "divide", {"assign c = a / b;"}, "div_sel"}},
27  {OpCode::AND, {"op_and", "and", {"assign c = a & b;"}, "and_sel"}},
28  {OpCode::OR, {"op_or", "or", {"assign c = a | b;"}, "or_sel"}},
29  {OpCode::XOR, {"op_xor", "xor", {"assign c = a ^ b;"}, "xor_sel"}},
30  {OpCode::SHL, {"op_shl", "shl", {"assign c = a << b;"}, "shl_sel"}},
31  {OpCode::LSHR, {"op_lshr", "lshr", {"assign c = a >> b;"}, "lshr_sel"}},
32  {OpCode::ASHR, {"op_ashr", "ashr", {"assign c = a >>> b;"}, "ashr_sel"}},
33  {OpCode::CONST, {"op_const", "const", {"//const;"}, "const_sel"}},
34  {OpCode::LOAD, {"op_load", "load", {"//load;"}, "load_sel"}},
35  {OpCode::STORE, {"op_store", "store", {"//store;"}, "store_sel"}},
36  {OpCode::ICMP, {"op_cmp", "cmp", {"assign c = a == b? 1: 0;"},"cmp_sel"}}
37 };
38 
39 // Returns a unique name for a funcunit
40 std::string FuncUnit::GenericName()
41 {
42  std::string NameToReturn = "func_" + std::to_string(getSize()) + "b"; // Base name is "func"
43  for (unsigned i = 0; i < supported_modes.size(); i++)
44  {
45  // Add a "_(module_name_inside)" to the name
46  NameToReturn.append("_");
47  NameToReturn.append(all_modes.at(supported_modes.at(i)).OpName);
48  }
49  return NameToReturn;
50 }
51 
52 // FuncUnit constructor
53 FuncUnit::FuncUnit(std::string name, Location loc, std::vector<OpGraphOpCode> supported_modes_, unsigned size, int II, int latency, bool isElastic)
54  : Module(name, loc, size, isElastic)
55  , pipeline_mode(II, latency)
56  , supported_modes(std::move(supported_modes_))
57 {
58  if (II != 1) {
59  std::cout << II << '\n';
60  make_and_throw<cgrame_error>([&](auto&& s) { s << "dont support an II other than 1 (given II=" << II << ')'; });
61  }
62 
63  parameterlist["latency"] = latency;
64 
65  // Create the ports
66  addPort("in_a", PORT_INPUT, "size", size);
67  addPort("in_b", PORT_INPUT, "size", size);
68 
69  if (supported_modes.size() > 1) // If there is more than one mode, we need to add a select line, we will also have a mux, so make port "out" a reg
70  {
71  addPort("select", PORT_CONFIG, ceil(log2(supported_modes.size())), false);
72  addPort("out", PORT_OUTPUT, "size", size);
73 
74  std::string width_suffix = "_" + std::to_string((int)size) + "b";
75  for (unsigned i = 0; i < supported_modes.size(); i++)
76  {
77  if (all_modes.at(supported_modes.at(i)).ModuleName == "op_nop" or all_modes.at(supported_modes.at(i)).ModuleName == "op_not"){
79  all_modes.at(supported_modes.at(i)).ModuleName + width_suffix, loc,
80  all_modes.at(supported_modes.at(i)).Functionality,
81  size), 0.1, static_cast<double>(i)/(supported_modes.size()), 0.9, static_cast<double>(i)/(supported_modes.size()));
82  }
83  else{
84  //create a new submodule to pointer (check if constructor is breaking, or if the add function is breaking)
86  all_modes.at(supported_modes.at(i)).ModuleName + width_suffix, loc,
87  all_modes.at(supported_modes.at(i)).Functionality,
88  size), 0.1, static_cast<double>(i)/(supported_modes.size()), 0.9, static_cast<double>(i)/(supported_modes.size()));
89  }
90  }
91 
92  //seperate the adding module and the creating case statement
93  addSubModule(new CaseStatement("caseStatement", loc, supported_modes.size(), size, latency), 0, 0.5, 0.1, 1);
94 
95  for (unsigned i = 0; i < supported_modes.size(); i++)
96  {
97  addConnection("this.in_a", all_modes.at(supported_modes.at(i)).ModuleName + width_suffix + ".a");
98 
99  if (all_modes.at(supported_modes.at(i)).ModuleName != "op_nop" and all_modes.at(supported_modes.at(i)).ModuleName != "op_not"){
100  addConnection("this.in_b", all_modes.at(supported_modes.at(i)).ModuleName + width_suffix + ".b");
101  }
102  }
103  for (unsigned i = 0; i < supported_modes.size(); i++)
104  {
105  addConnection(all_modes.at(supported_modes.at(i)).ModuleName + width_suffix + ".c", "caseStatement.in" + std::to_string(i));
106  }
107  addConnection("this.select", "caseStatement.select");
108  addConnection("caseStatement.out", "this.out");
109  }
110  else if (supported_modes.size() == 1)
111  {
112 
113  addPort("out", PORT_OUTPUT, "size", size);
114 
115  std::string width_suffix = "_" + std::to_string((int)size) + "b";
116  if (all_modes.at(supported_modes.at(0)).ModuleName == "op_nop" or all_modes.at(supported_modes.at(0)).ModuleName == "op_not"){
118  all_modes.at(supported_modes.at(0)).ModuleName + width_suffix, loc,
119  all_modes.at(supported_modes.at(0)).Functionality,
120  size), 0.1, static_cast<double>(0)/(supported_modes.size()), 0.9, static_cast<double>(1)/(supported_modes.size()));
121  }
122  else{
124  all_modes.at(supported_modes.at(0)).ModuleName + width_suffix, loc,
125  all_modes.at(supported_modes.at(0)).Functionality,
126  size), 0.1, static_cast<double>(0)/(supported_modes.size()), 0.9, static_cast<double>(1)/(supported_modes.size()));
127  }
128  }
129  else if (supported_modes.size() < 1) // If there is jsut 1 mode, "out" can be just a wire
130  {
131  throw cgrame_error("not enough supported modes in the list");
132  }
133  else // Otherwise for 0 modes, "out" can just be a wire
134  {
135  addPort("out", PORT_OUTPUT, "size", size);
136  }
137 
138 
139  // Add position for visualization
140  node_relative_position.insert({"in_a", {0.33, 0}});
141  node_relative_position.insert({"in_b", {0.67, 0}});
142  node_relative_position.insert({"fu", {0.5, 0.33}});
143  node_relative_position.insert({"out", {0.5, 0.67}});
144 }
145 
147  const MRRG& mrrg, const OpGraph & og,
148  const Mapping& map,
149  const ConfigCell& ccell,
150  const MRRGNodesFromOpNode& mrrg_nodes_from_op_node,
151  const MRRGNodesFromValNode& mrrg_nodes_from_val_node
152 ) const {
153  (void)ccell;
154  (void)mrrg_nodes_from_val_node;
155  const auto bits_needed = std::lround(ceil(log2(supported_modes.size())));
156 
157  BitConfig bitConfig(mrrg.initiationInterval());
158 
159  // Organize nodes mapping by cycle
160  std::vector<MRRGNodesFromOpNode> opNodesByCycle(mrrg.initiationInterval());
161  for (const auto& op_and_mrrg_nodes : mrrg_nodes_from_op_node) {
162  for (const auto& mrrg_node : op_and_mrrg_nodes.second) {
163  opNodesByCycle[mrrg_node->cycle][op_and_mrrg_nodes.first].insert(mrrg_node);
164  }
165  }
166 
167  int cycle = 0;
168  int used_cycles = 0;
169  for (auto & op_and_mrrg_nodes : opNodesByCycle) {
170  if (op_and_mrrg_nodes.empty()) {
171  bitConfig.add({ (size_t)bits_needed, BitSetting::DONT_CARE_PREFER_LOW}, cycle);
172  } else if (op_and_mrrg_nodes.size() == 1) {
173  used_cycles++;
174  const auto find_result = std::find(begin(supported_modes), end(supported_modes), begin(op_and_mrrg_nodes)->first->opcode);
175  if (find_result == end(supported_modes)) {
176  throw cgrame_error("couldn't find op in supported modes list");
177  } else {
178  bitConfig.add( bitsettings_from_int(std::distance(begin(supported_modes), find_result), bits_needed), cycle);
179  }
180 
181  } else {
182  throw cgrame_error("expect either 0 or 1 op nodes");
183  }
184  ++cycle;
185  }
186  bitConfig.setUsedCycles(used_cycles);
187 
188  return bitConfig;
189 }
190 
192 {
193 }
194 
195 MRRG* FuncUnit::createMRRG(unsigned II = 1)
196 {
197  MRRG* result_ptr = new MRRG(II);
198  auto& result = *result_ptr;
199 
200  for(unsigned i = 0; i < II; i+= getII())
201  {
202  // create nodes
204 
206  MRRG::NodeDescriptor fu = result.insert(MRRGNode::make_function(this, data_size, i, "fu", getLatency(), supported_modes)).first;
207 
208  MRRG::NodeDescriptor out = result.insert(MRRGNode::make_routing(this, data_size, i, "out")).first;
209 
210  result.link(in_a, fu);
211  result.link(in_b, fu);
212  (void)out;
213  }
214 
215  for(unsigned i = 0; i < II; i+= getII())
216  {
217  MRRG::NodeDescriptor fu = result.getNode(i, "fu");
218  MRRG::NodeDescriptor out_next = result.getNode(MOD_II(i + getLatency()), "out");
219 
220  result.link(fu, out_next);
221  }
222 
223  return result_ptr;
224 }
225 
226 MemoryUnit::MemoryUnit(std::string name, Location loc, unsigned size, bool isElastic, bool pred)
227  : Module(name, loc, size, isElastic)
228  , pred(pred)
229 {
230  // Create the ports
231  addPort("addr", PORT_INPUT, "size", size, isElastic); //FIXME: size shouldn't be left as default
232  addPort("data_in", PORT_INPUT, "size", size, isElastic);
233  addPort("data_out", PORT_OUTPUT, "size", size, isElastic);
234  addPort("w_rq", PORT_CONFIG, 1, isElastic);
235  if (pred) {
236  addPort("pred", PORT_INPUT, 1, isElastic);
237  }
238 
239  addVerilogPort("addr_to_ram", PORT_OUTPUT, "size", size);
240  addVerilogPort("data_in_to_ram", PORT_OUTPUT, "size", size);
241  addVerilogPort("data_out_from_ram", PORT_INPUT, "size", size);
242  addVerilogPort("w_rq_to_ram", PORT_OUTPUT, "", 1);
243  if (isElastic) {
244  addVerilogPort("ram_input_ready", PORT_INPUT, "", 1);
245  addVerilogPort("ram_input_enable", PORT_OUTPUT, "", 1);
246  addVerilogPort("ram_output_ready", PORT_OUTPUT, "", 1);
247  addVerilogPort("ram_output_enable", PORT_INPUT, "", 1);
248  }
249 
250  // Add position for visualization
251  node_relative_position.insert({"mem", {0.33, 0.5}});
252  node_relative_position.insert({"addr", {0, 0.33}});
253  node_relative_position.insert({"data_in", {0, 0.67}});
254  node_relative_position.insert({"data_out", {0.67, 0.5}});
255 }
256 
258 {
259  std::string elas = isElastic ? "elastic_" : "";
260  return elas + "memUnit_" + std::to_string(getSize()) + "b";
261 }
262 
264 {
265  std::cout << std::endl;
266  std::cout << SET_INDENT << "// ALERT: This module is an unimplemented place holder.\n";
267 }
268 
269 // CoreIR Implementation of GenFunctionality
270 nlohmann::json MemoryUnit::CoreIRGenFunctionality() // Virtual function override
271 {
272  nlohmann::json vjson;
273 
274  // Create Header
275  vjson["prefix"] = "cgrame_"; //module prefix
276  vjson["parameters"] = {};
277  for (auto& parameter : parameterlist)
278  {
279  vjson["parameters"].push_back(parameter.first);
280  }
281  vjson["interface"] = {};
282  for (auto& port : ports)
283  {
284  std::string portName = port.second->getName();
285  vjson["interface"].push_back(portName);
286  }
287  for (auto& port : portsToPropagate)
288  {
289  std::string portName = port.getName();
290  vjson["interface"].push_back(portName);
291  }
292 
293  // module definition
294  std::string moduleDefinition;
295  for (auto& port : ports)
296  {
297  port_type portType = port.second->pt;
298  std::string portTypeString = {};
299  if(portType == port_type::PORT_INPUT)
300  {
301  portTypeString = "input";
302  }
303  else if (portType == port_type::PORT_OUTPUT)
304  {
305  portTypeString = "output";
306  }
307  else if (portType == port_type::PORT_OUTPUT_REG)
308  {
309  portTypeString = "output reg";
310  }
311  else
312  {
313  portTypeString = "inout";
314  }
315  std::string portSizeString;
316  if (!(port.second->parameter).empty()) // Check if size is parameterized
317  {
318  std::string portParameterName = port.second->parameter;
319  portSizeString = "[" + portParameterName + "-1:0]";
320  }
321  else
322  {
323  portSizeString = "[" + std::to_string(port.second->size - 1) + ":0]";
324  }
325  std::string portName = port.second->getName();
326  std::string portDeclaration = portTypeString + " " + portSizeString + " " + portName + ";\n";
327  moduleDefinition += std::string(SET_INDENT) + portDeclaration;
328  }
329 
330  for (auto& port : portsToPropagate)
331  {
332  port_type portType = port.pt;
333  std::string portTypeString = {};
334  if(portType == port_type::PORT_INPUT)
335  {
336  portTypeString = "input";
337  }
338  else if (portType == port_type::PORT_OUTPUT)
339  {
340  portTypeString = "output";
341  }
342  else if (portType == port_type::PORT_OUTPUT_REG)
343  {
344  portTypeString = "output reg";
345  }
346  else
347  {
348  portTypeString = "inout";
349  }
350  std::string portSizeString;
351  if (!(port.parameter).empty()) // Check if size is parameterized
352  {
353  std::string portParameterName = port.parameter;
354  portSizeString = "[" + portParameterName + "-1:0]";
355  }
356  else
357  {
358  portSizeString = "[" + std::to_string(port.size - 1) + ":0]";
359  }
360  std::string portName = port.getName();
361  std::string portDeclaration = portTypeString + " " + portSizeString + " " + portName + ";\n";
362  moduleDefinition += std::string(SET_INDENT) + portDeclaration;
363  }
364 
365  // Functionality
366  if (isElastic) {
367  moduleDefinition += std::string(SET_INDENT) + "assign addr_to_ram = addr;\n";
368  moduleDefinition += std::string(SET_INDENT) + "assign data_in_to_ram = data_in;\n";
369  if (pred) {
370  moduleDefinition += std::string(SET_INDENT) + "assign w_rq_to_ram = w_rq & pred;\n";
371  } else {
372  moduleDefinition += std::string(SET_INDENT) + "assign w_rq_to_ram = w_rq;\n";
373  }
374  moduleDefinition += std::string(SET_INDENT) + "assign ram_input_enable = addr_valid_upstream;\n";
375  moduleDefinition += std::string(SET_INDENT) + "assign ram_output_ready = ~data_out_stop_downstream;\n";
376  moduleDefinition += std::string(SET_INDENT) + "assign addr_stop_upstream = ~ram_input_ready;\n";
377  moduleDefinition += std::string(SET_INDENT) + "assign data_in_stop_upstream = ~ram_input_ready;\n";
378  moduleDefinition += std::string(SET_INDENT) + "assign data_out = data_out_from_ram;\n";
379  moduleDefinition += std::string(SET_INDENT) + "assign data_out_valid_downstream = ram_output_enable;";
380  } else {
381  moduleDefinition += std::string(SET_INDENT) + "assign addr_to_ram = addr;\n";
382  moduleDefinition += std::string(SET_INDENT) + "assign data_in_to_ram = data_in;\n";
383  if (pred) {
384  moduleDefinition += std::string(SET_INDENT) + "assign w_rq_to_ram = w_rq & pred;\n";
385  } else {
386  moduleDefinition += std::string(SET_INDENT) + "assign w_rq_to_ram = w_rq;\n";
387  }
388  moduleDefinition += std::string(SET_INDENT) + "assign data_out = data_out_from_ram;";
389  }
390 
391 
392  vjson["definition"] = moduleDefinition;
393  return vjson;
394 }
395 
397  const MRRG& mrrg, const OpGraph & og,
398  const Mapping& map,
399  const ConfigCell& ccell,
400  const MRRGNodesFromOpNode& mrrg_nodes_from_op_node,
401  const MRRGNodesFromValNode& mrrg_nodes_from_val_node
402 ) const {
403  (void)ccell;
404  (void)mrrg_nodes_from_val_node;
405  static const std::vector<std::pair<OpGraphOpCode,std::regex>> node_name_checks {
406  {OpCode::LOAD, std::regex("\\.data_out$")},
407  {OpCode::STORE, std::regex("\\.data_in$")},
408  };
409 
410  BitConfig bitConfig(mrrg.initiationInterval());
411 
412  // Organize nodes mapping by cycle
413  std::vector<MRRGNodesFromOpNode> opNodesByCycle(mrrg.initiationInterval());
414  for (const auto& op_and_mrrg_nodes : mrrg_nodes_from_op_node) {
415  for (const auto& mrrg_node : op_and_mrrg_nodes.second) {
416  opNodesByCycle[mrrg_node->cycle][op_and_mrrg_nodes.first].insert(mrrg_node);
417  }
418  }
419 
420  int used_cycles = 0;
421  // TODO: Fixup for multi-context cases
422  for (int cycle = 0; cycle < mrrg.initiationInterval(); cycle++) {
423  MRRGNodesFromOpNode mrrg_nodes_from_op_node_in_cycle = opNodesByCycle[cycle];
424 
425  if (mrrg_nodes_from_op_node_in_cycle.empty()) {
426  bitConfig.add({ BitSetting::LOW }, cycle);
427 
428  } else {
429  const OpGraphOp& op = [&]() -> decltype(auto) {
430  if (mrrg_nodes_from_op_node_in_cycle.size() == 1) {
431  return *(begin(mrrg_nodes_from_op_node_in_cycle)->first);
432  } else {
433  throw cgrame_error("expected exactly one op node");
434  }
435  }();
436  used_cycles++;
437  // Set bit config for write enable to high if op is store.
438  bitConfig.add({ from_bool<BitSetting>(op.opcode == OpCode::STORE) }, cycle);
439  }
440  }
441  bitConfig.setUsedCycles(used_cycles);
442  return bitConfig;
443 }
444 
445 MRRG* MemoryUnit::createMRRG(unsigned int II)
446 {
447  MRRG* result_ptr = new MRRG(II);
448  auto& result = *result_ptr;
449 
450  for(unsigned i = 0; i < II; i++)
451  {
452  // create FU node
453  MRRGNode& fu = result.getNodeRef(result.insert(MRRGNode::make_function(this, data_size, i, "mem", 1,
454  {OpCode::LOAD, OpCode::STORE}
455  )).first);
456  if (pred) {
457  MRRG::NodeDescriptor pred = result.insert(MRRGNode::make_operand_pin(this, data_size, i, "pred", {Operands::PREDICATE})).first;
458  result.link(pred, &fu);
459  }
460 
461  // create input nodes and connections
462  MRRG::NodeDescriptor addr = result.insert(MRRGNode::make_operand_pin(this, data_size, i, "addr", {Operands::MEM_ADDR})).first;
463  result.link(addr, &fu);
464 
465  MRRG::NodeDescriptor data_in = result.insert(MRRGNode::make_operand_pin(this, data_size, i, "data_in", {Operands::MEM_DATA})).first;
466  result.link(data_in, &fu);
467 
468  // create output nodes and connections
469  MRRG::NodeDescriptor data_out = result.insert(MRRGNode::make_routing(this, data_size, MOD_II(i + 1), "data_out")).first;
470  result.link(&fu, data_out);
471  }
472 
473  return result_ptr;
474 }
475 
477 {
478 }
479 
480 ConstUnit::ConstUnit(std::string name, Location loc, int size, int II, bool isElastic)
481  : Module(name, loc, size, isElastic)
482 {
483  addPort("out", PORT_OUTPUT, "size", size, isElastic);
484  if (II > 1)
485  addPort("Context", PORT_INPUT, ceil(log2(II)));
486  if (isElastic) {
487  addSubModule(new DisconnectedSource("dis", loc, 1, 1));
488  addConnection("dis.out", "this.out_valid_downstream", false);
489  }
490 
491  // Add position for visualization
492  node_relative_position.insert({"const", {0, 0.25}});
493  node_relative_position.insert({"out", {0, 0.75}});
494 
495  // FIXME:
496  // The second argument in the ConfigCell constructor below actually does
497  // not make a Verilog connection to the port correctly. But this should
498  // only affect Verilog generation flow. The actual Verilog connection
499  // assignment is hardcoded in ConstUnit::GenFunctionality().
500  addConfig("ConstVal", {"this.out"}, II, false);
501  if (II > 1) {
502  addConnection("this.Context", "ConstVal.Context", false);
503  }
504 }
505 
507 {
508  std::string elas = isElastic ? "elastic_" : "";
509  return elas + "const_" + std::to_string(getSize()) + "b";
510 }
511 
513 {
514  // FIXME:
515  // Once Module::addConfig() is fixed to accept ports as connection, this
516  // method should be removed.
517  std::cout << std::endl;
518  std::cout << SET_INDENT << "assign out = ConstVal_sig;";
519  std::cout << std::endl;
520 }
521 
523  const MRRG& mrrg, const OpGraph & og,
524  const Mapping& map,
525  const ConfigCell& ccell,
526  const MRRGNodesFromOpNode& mrrg_nodes_from_op_node,
527  const MRRGNodesFromValNode& mrrg_nodes_from_val_node
528 ) const {
529  (void)ccell;
530 
531  BitConfig bitConfig(mrrg.initiationInterval());
532 
533  // Organize nodes mapping by cycle
534  std::vector<MRRGNodesFromOpNode> opNodesByCycle(mrrg.initiationInterval());
535  for (const auto& op_and_mrrg_nodes : mrrg_nodes_from_op_node) {
536  for (const auto& mrrg_node : op_and_mrrg_nodes.second) {
537  opNodesByCycle[mrrg_node->cycle][op_and_mrrg_nodes.first].insert(mrrg_node);
538  }
539  }
540 
541  int cycle = 0;
542  int used_cycles = 0;
543  for (auto & mrrg_nodes_from_op_node_per_cycle : opNodesByCycle) {
544  if (mrrg_nodes_from_op_node_per_cycle.empty()) {
545  bitConfig.add({data_size, BitSetting::LOW}, cycle);
546  } else {
547  if (mrrg_nodes_from_op_node_per_cycle.size() != 1) {
548  make_and_throw<std::logic_error>([&](auto&& s) {
549  s << "This opgraph_op node is mapped to multiple MRRG nodes\n";
550  });
551  }
552  else if (std::begin(mrrg_nodes_from_op_node_per_cycle)->first->opcode != OpCode::CONST) {
553  make_and_throw<std::logic_error>([&](auto&& s) {
554  s << "A non constant op was mapped to this const unit\n";
555  });
556  }
557  used_cycles++;
558  std::vector<BitSetting> result = bitsettings_from_int(std::begin(mrrg_nodes_from_op_node_per_cycle)->first->constVal, this->data_size);
559  bitConfig.add(result, cycle);
560  }
561  ++cycle;
562  }
563  bitConfig.setUsedCycles(used_cycles);
564  return bitConfig;
565 }
566 
567 MRRG* ConstUnit::createMRRG(unsigned int II)
568 {
569  MRRG* result_ptr = new MRRG(II);
570  auto& result = *result_ptr;
571 
572  for(unsigned i = 0; i < II; i++)
573  {
574  // create FU node
575  MRRG::NodeDescriptor fu = result.insert(MRRGNode::make_function(this, data_size, i, "const", 0, {OpCode::CONST}, 1, true)).first;
576  // create output nodes and connections
577  MRRG::NodeDescriptor data_out = result.insert(MRRGNode::make_routing(this, data_size, i, "out")).first;
578  result.link(fu, data_out);
579  }
580 
581  return result_ptr;
582 }
583 
585 {
586 }
587 
588 /************ CaseStatement **********/
589 CaseStatement::CaseStatement(std::string name, Location loc, unsigned numInputs, unsigned size, int latency, bool isElastic)
590  : Module(name, loc, size, isElastic)
591  , numInputs(numInputs)
592  , latency(latency)
593 {
595 
596  addParameter("size", size);
597  addParameter("latency", latency);
598 
599  for (unsigned i = 0; i < numInputs; i++)
600  {
601  addPort("in" + std::to_string(i), PORT_INPUT, "size", size);
602  }
603  addPort("select", PORT_CONFIG, ceil(log2(numInputs)), isElastic);
604  addPort("out", PORT_OUTPUT, "size", size, isElastic);
605 }
606 
608 {
609  return "caseStatement_" + std::to_string(numInputs) + "in_" + std::to_string(latency) + "latency_" + std::to_string(getSize()) + "b";
610 }
611 
613 {
614  std::cout << "\n" << SET_INDENT << "always @*\n";
615  std::cout << SET_DOUBLE_INDENT << "case (select)\n";
616  if (getNumInputs() == 1) // We do not want a case statement, because the select variable does not exist
617  {
618  std::cout << SET_INDENT << "assign computation = in0;\n";
619  }
620  else
621  {
622  for (unsigned i = 0; i < getNumInputs(); i++)
623  std::cout << SET_TRIPLE_INDENT << i << ": computation = in" << i << ";\n";
624  std::cout << SET_TRIPLE_INDENT << "default: computation = {size{1'bx}};\n";
625  std::cout << SET_DOUBLE_INDENT << "endcase\n";
626  }
627  std::cout << '\n';
628 
629  std::cout
630  << SET_INDENT << "integer i;\n"
631  << SET_INDENT << "always @(posedge CGRA_Clock, posedge CGRA_Reset) begin\n"
632  << SET_INDENT << " if (CGRA_Reset) begin\n"
633  << SET_INDENT << " for (i=0; i < latency; i=i+1)\n"
634  << SET_INDENT << " delay_ppln[i] <= {size{1'b0}};\n"
635  << SET_INDENT << " end else begin\n"
636  << SET_INDENT << " for (i=1; i < latency; i=i+1)\n"
637  << SET_INDENT << " delay_ppln[i] <= delay_ppln[i-1];\n"
638  << SET_INDENT << " delay_ppln[0] <= computation;\n"
639  << SET_INDENT << " end\n"
640  << SET_INDENT << "end\n"
641  << '\n'
642  << SET_INDENT << "generate\n"
643  << SET_INDENT << " if (latency > 0)\n"
644  << SET_INDENT << " assign out = delay_ppln[latency-1];\n"
645  << SET_INDENT << " else\n"
646  << SET_INDENT << " assign out = computation;\n"
647  << SET_INDENT << "endgenerate\n"
648  ;
649 }
650 
651 nlohmann::json CaseStatement::CoreIRGenFunctionality() // Virtual function override
652 {
653  nlohmann::json vjson;
654 
655  // Create Header
656  vjson["prefix"] = "cgrame_"; //module prefix
657  vjson["parameters"] = {};
658  for (auto& parameter : parameterlist)
659  {
660  vjson["parameters"].push_back(parameter.first);
661  }
662  vjson["interface"] = {};
663  vjson["interface"].push_back("CGRA_Clock");
664  vjson["interface"].push_back("CGRA_Reset");
665  vjson["interface"].push_back("CGRA_Enable");
666  for (auto& port : ports)
667  {
668  std::string portName = port.second->getName();
669  vjson["interface"].push_back(portName);
670  }
671 
672  // module definition
673  std::string moduleDefinition;
674  moduleDefinition += std::string(SET_INDENT) + "input CGRA_Clock;\n";
675  moduleDefinition += std::string(SET_INDENT) + "input CGRA_Reset;\n";
676  moduleDefinition += std::string(SET_INDENT) + "input CGRA_Enable;\n";
677  for (auto& port : ports)
678  {
679  port_type portType = port.second->pt;
680  std::string portTypeString = {};
681  if(portType == port_type::PORT_INPUT)
682  {
683  portTypeString = "input";
684  }
685  else if (portType == port_type::PORT_OUTPUT)
686  {
687  portTypeString = "output";
688  }
689  else if (portType == port_type::PORT_OUTPUT_REG)
690  {
691  portTypeString = "output reg";
692  }
693  else
694  {
695  portTypeString = "inout";
696  }
697  std::string portSizeString;
698  if (!(port.second->parameter).empty()) // Check if size is parameterized
699  {
700  std::string portParameterName = port.second->parameter;
701  portSizeString = "[" + portParameterName + "-1:0]";
702  }
703  else
704  {
705  portSizeString = "[" + std::to_string(port.second->size - 1) + ":0]";
706  }
707  std::string portName = port.second->getName();
708  std::string portDeclaration = portTypeString + " " + portSizeString + " " + portName + ";\n";
709  moduleDefinition += std::string(SET_INDENT) + portDeclaration;
710  }
711 
712  // Declare wires
713  moduleDefinition += std::string(SET_INDENT) + "reg [size-1:0] computation;\n";
714  moduleDefinition += std::string(SET_INDENT) + "reg [size-1:0] delay_ppln [latency-1:0];\n";
715 
716  // Functionality
717  moduleDefinition += std::string(SET_INDENT) + "always @*\n";
718  moduleDefinition += std::string(SET_DOUBLE_INDENT) + "case (select)\n";
719  for (unsigned i = 0; i < getNumInputs(); i++)
720  {
721  moduleDefinition += std::string(SET_TRIPLE_INDENT) + std::to_string(i) + ": computation = in" + std::to_string(i) + ";\n";
722  }
723  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "default: computation = {size{1'bx}};\n";
724  moduleDefinition += std::string(SET_DOUBLE_INDENT) + "endcase\n";
725 
726  moduleDefinition += std::string(SET_INDENT) + "integer i;\n";
727  moduleDefinition += std::string(SET_INDENT) + "always @(posedge CGRA_Clock, posedge CGRA_Reset) begin\n";
728  moduleDefinition += std::string(SET_DOUBLE_INDENT) + "if (CGRA_Reset) begin\n";
729  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "for (i=0; i < latency; i=i+1)\n";
730  moduleDefinition += std::string(SET_QUAD_INDENT) + "delay_ppln[i] <= {size{1'b0}};\n";
731  moduleDefinition += std::string(SET_DOUBLE_INDENT) + "end else if (CGRA_Enable) begin\n";
732  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "for (i=1; i < latency; i=i+1)\n";
733  moduleDefinition += std::string(SET_QUAD_INDENT) + "delay_ppln[i] <= delay_ppln[i-1];\n";
734  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "delay_ppln[0] <= computation;\n";
735  moduleDefinition += std::string(SET_DOUBLE_INDENT) + "end\n";
736  moduleDefinition += std::string(SET_INDENT) + "end\n\n";
737 
738  moduleDefinition += std::string(SET_INDENT) + "generate\n";
739  moduleDefinition += std::string(SET_DOUBLE_INDENT) + "if (latency > 0)\n";
740  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "assign out = delay_ppln[latency-1];\n";
741  moduleDefinition += std::string(SET_DOUBLE_INDENT) + "else\n";
742  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "assign out = computation;\n";
743  moduleDefinition += std::string(SET_INDENT) + "endgenerate";
744 
745  if (isElastic){
746  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "assign select_valid_upstream = 1'b1;\n";
747  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "assign out_valid_downstream = 1'b1;\n";
748  moduleDefinition += std::string(SET_TRIPLE_INDENT) + "assign select_stop_upstream = out_stop_downstream;\n";
749  }
750 
751  vjson["definition"] = moduleDefinition;
752  return vjson;
753 }
754 
756 {
757 }
BitConfig
Definition: BitSetting.h:58
ConfigCell
Definition: Module.h:825
OpCode::DIV
@ DIV
ConstUnit::ConstUnit
ConstUnit(std::string name, Location, int size=DEFAULT_SIZE, int contexts=1, bool isElastic=false)
Definition: ModuleFuncUnit.cpp:480
CaseStatement::CoreIRGenFunctionality
virtual nlohmann::json CoreIRGenFunctionality() override
Definition: ModuleFuncUnit.cpp:651
FuncUnit::getBitConfig
virtual BitConfig getBitConfig(const MRRG &mrrg, const OpGraph &og, const Mapping &map, const ConfigCell &ccell, const MRRGNodesFromOpNode &mrrg_nodes_from_op_node, const MRRGNodesFromValNode &mrrg_nodes_from_val_node) const override
Definition: ModuleFuncUnit.cpp:146
CaseStatement::latency
int latency
Definition: Module.h:895
OpCode::CONST
@ CONST
SET_TRIPLE_INDENT
const char *const SET_TRIPLE_INDENT
Definition: Module.h:39
MRRG
Definition: MRRG.h:216
PORT_OUTPUT_REG
@ PORT_OUTPUT_REG
Definition: Module.h:65
MRRGNodesFromOpNode
std::map< OpGraphOp *, std::set< MRRG::NodeDescriptor > > MRRGNodesFromOpNode
Definition: Module.h:146
Location
Definition: Module.h:156
Module::data_size
unsigned data_size
Definition: Module.h:338
CaseStatement::getNumInputs
unsigned getNumInputs()
Definition: Module.h:892
Operands::BINARY_LHS
static constexpr auto & BINARY_LHS
Definition: OpGraph.h:89
OpCode::ASHR
@ ASHR
OpCode::OR
@ OR
Module::addConnection
void addConnection(std::string src, std::string dst, bool isInMRRG=true)
Definition: Module.cpp:1241
begin
auto begin(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:137
Module.h
Module::addParameter
void addParameter(std::string parameterName, unsigned parameterValue)
Definition: Module.cpp:1503
Module::adds_synchronous_circuitry
bool adds_synchronous_circuitry
Definition: Module.h:370
OpCode::STORE
@ STORE
Module::parameterlist
std::map< std::string, unsigned > parameterlist
Definition: Module.h:224
BitSetting::DONT_CARE_PREFER_LOW
@ DONT_CARE_PREFER_LOW
ConstUnit::getBitConfig
virtual BitConfig getBitConfig(const MRRG &mrrg, const OpGraph &og, const Mapping &map, const ConfigCell &ccell, const MRRGNodesFromOpNode &mrrg_nodes_from_op_node, const MRRGNodesFromValNode &mrrg_nodes_from_val_node) const override
Definition: ModuleFuncUnit.cpp:522
BitConfig::add
void add(std::vector< BitSetting > bitsetting, int cycle)
Definition: BitSetting.h:63
OpCode::ADD
@ ADD
Module::addPort
void addPort(std::string portname, port_type pt, unsigned size)
Definition: Module.cpp:1354
Module::node_relative_position
std::map< std::string, VisualPositionPoint > node_relative_position
Definition: Module.h:360
ConstUnit::createMRRG
MRRG * createMRRG(unsigned II) override
Definition: ModuleFuncUnit.cpp:567
CustomModule
Definition: Module.h:792
MemoryUnit::GenericName
virtual std::string GenericName() override
Definition: ModuleFuncUnit.cpp:257
from_bool< BitSetting >
BitSetting from_bool< BitSetting >(bool b)
Definition: BitSetting.cpp:17
CaseStatement::GenericName
virtual std::string GenericName() override
Definition: ModuleFuncUnit.cpp:607
to_string
const std::string & to_string(const OpGraphOpCode &opcode)
Definition: OpGraph.cpp:111
ConstUnit::~ConstUnit
virtual ~ConstUnit()
Definition: ModuleFuncUnit.cpp:584
OpCode::LOAD
@ LOAD
MRRGNodesFromValNode
std::map< OpGraphVal *, std::set< MRRG::NodeDescriptor > > MRRGNodesFromValNode
Definition: Module.h:147
OpCode::LSHR
@ LSHR
OpCode::NOP
@ NOP
BitSetting::LOW
@ LOW
Mapping
Definition: Mapping.h:31
CaseStatement
Definition: Module.h:885
MOD_II
#define MOD_II(x)
Definition: Module.h:32
PORT_CONFIG
@ PORT_CONFIG
Definition: Module.h:67
MemoryUnit::~MemoryUnit
virtual ~MemoryUnit()
Definition: ModuleFuncUnit.cpp:476
OpCode::XOR
@ XOR
Operands::BINARY_RHS
static constexpr auto & BINARY_RHS
Definition: OpGraph.h:90
CaseStatement::GenFunctionality
virtual void GenFunctionality() override
Definition: ModuleFuncUnit.cpp:612
MRRGNode::make_operand_pin
static MRRGNode make_operand_pin(Module *parent, int bitwidth, int cycle, STR &&name, SupportedOpTags operand_tags, int latency=0, int max_cap=1)
Definition: MRRG.h:164
MRRGNode::make_function
static MRRGNode make_function(Module *parent, int bitwidth, int cycle, STR &&name, int latency, SupportedOps supported_ops, int max_cap=1, bool is_const_unit=false)
Definition: MRRG.h:191
CaseStatement::numInputs
unsigned numInputs
Definition: Module.h:892
ConstUnit::GenFunctionality
virtual void GenFunctionality() override
Definition: ModuleFuncUnit.cpp:512
SET_INDENT
const char *const SET_INDENT
Definition: Module.h:37
Module
Definition: Module.h:163
PORT_INPUT
@ PORT_INPUT
Definition: Module.h:63
FuncUnit::~FuncUnit
virtual ~FuncUnit()
Definition: ModuleFuncUnit.cpp:191
Module::portsToPropagate
std::vector< Port > portsToPropagate
Definition: Module.h:229
Module::addVerilogPort
void addVerilogPort(std::string name, port_type pt, std::string parameter, unsigned size)
Definition: Module.cpp:1515
FuncUnit::createMRRG
virtual MRRG * createMRRG(unsigned II) override
Definition: ModuleFuncUnit.cpp:195
OpCode::SHL
@ SHL
MemoryUnit::getBitConfig
virtual BitConfig getBitConfig(const MRRG &mrrg, const OpGraph &og, const Mapping &map, const ConfigCell &ccell, const MRRGNodesFromOpNode &mrrg_nodes_from_op_node, const MRRGNodesFromValNode &mrrg_nodes_from_val_node) const override
Definition: ModuleFuncUnit.cpp:396
FuncUnit::all_modes
static const std::map< OpGraphOpCode, LLVMMode > all_modes
Definition: Module.h:432
Module::addConfig
void addConfig(ConfigCell *c, std::vector< std::string > ConnectTo)
Definition: Module.cpp:1087
end
auto end(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:138
FuncUnit::getLatency
int getLatency() const
Definition: Module.h:429
OpGraphOp
Definition: OpGraph.h:131
MRRGNode
Definition: MRRG.h:60
Module::getSize
int getSize() const
Definition: Module.h:246
FuncUnit::getII
int getII() const
Definition: Module.h:428
CaseStatement::~CaseStatement
virtual ~CaseStatement()
Definition: ModuleFuncUnit.cpp:755
MemoryUnit::createMRRG
MRRG * createMRRG(unsigned II) override
Definition: ModuleFuncUnit.cpp:445
Module::addSubModule
void addSubModule(Module *m)
Definition: Module.cpp:1124
BitConfig::setUsedCycles
void setUsedCycles(int uc)
Definition: BitSetting.h:67
CaseStatement::CaseStatement
CaseStatement(std::string name, Location, unsigned numInputs, unsigned size, int latency, bool isElastic=false)
Definition: ModuleFuncUnit.cpp:589
OpCode::AND
@ AND
Module::isElastic
bool isElastic
Definition: Module.h:236
SET_DOUBLE_INDENT
const char *const SET_DOUBLE_INDENT
Definition: Module.h:38
Module::ports
std::map< std::string, Port * > ports
Definition: Module.h:225
MemoryUnit::pred
bool pred
Definition: Module.h:537
OpCode::SUB
@ SUB
OpCode::MUL
@ MUL
Module::loc
Location loc
Definition: Module.h:239
MRRG::initiationInterval
int initiationInterval() const
Definition: MRRG.h:346
FuncUnit::FuncUnit
FuncUnit(std::string name, Location, std::vector< OpGraphOpCode > supported_modes, unsigned size, int II, int latency, bool isElastic=false)
Definition: ModuleFuncUnit.cpp:53
OpCode::ICMP
@ ICMP
DisconnectedSource
For when an input is unused, but a connection is required anyway.
Definition: Module.h:771
ConstUnit::GenericName
virtual std::string GenericName() override
Definition: ModuleFuncUnit.cpp:506
Operands::MEM_DATA
static constexpr auto & MEM_DATA
Definition: OpGraph.h:94
MRRGNode::make_routing
static MRRGNode make_routing(Module *parent, int bitwidth, int cycle, STR &&name, int latency=0, int max_cap=1)
Definition: MRRG.h:151
Operands::BINARY_ANY
static constexpr auto & BINARY_ANY
Definition: OpGraph.h:91
port_type
port_type
Definition: Module.h:61
MemoryUnit::CoreIRGenFunctionality
virtual nlohmann::json CoreIRGenFunctionality() override
Definition: ModuleFuncUnit.cpp:270
FuncUnit::supported_modes
std::vector< OpGraphOpCode > supported_modes
Definition: Module.h:435
MemoryUnit::MemoryUnit
MemoryUnit(std::string name, Location, unsigned size=DEFAULT_SIZE, bool isElastic=false, bool pred=false)
Definition: ModuleFuncUnit.cpp:226
MemoryUnit::GenFunctionality
virtual void GenFunctionality() override
Definition: ModuleFuncUnit.cpp:263
CustomModuleSingleInput
Definition: Module.h:803
Operands::PREDICATE
static constexpr auto & PREDICATE
Definition: OpGraph.h:88
OpGraph
Definition: OpGraph.h:215
OpGraphOp::opcode
OpGraphOpCode opcode
Definition: OpGraph.h:158
FuncUnit::GenericName
virtual std::string GenericName() override
Definition: ModuleFuncUnit.cpp:40
cgrame_error
Definition: Exception.h:20
PORT_OUTPUT
@ PORT_OUTPUT
Definition: Module.h:64
Operands::MEM_ADDR
static constexpr auto & MEM_ADDR
Definition: OpGraph.h:93
SET_QUAD_INDENT
const char *const SET_QUAD_INDENT
Definition: Module.h:40
bitsettings_from_int
std::vector< BitSetting > bitsettings_from_int(const INTEGRAL &value, int num_bits)
Definition: BitSetting.h:52