sdf

Dependencies:   AvailableMemory mbed-rtos mbed

Revision:
0:1c8f2727e9f5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDF.cpp	Thu Apr 03 22:56:32 2014 +0000
@@ -0,0 +1,563 @@
+#include "SDF.h"
+
+/*set the input to a ring buffer pointed to by buf, the input node will read input from this ring buffer*/
+void INode::setInput(RingBuffer *buf){
+  input=buf;
+}
+
+/*each time inode executes, reading one sample from input ring buffer, and put this data onto all the outgoing edges*/
+void INode::execute(){
+  int inputSample = input->next();
+  for(int i=0; i<(int)outbounds.size(); i++){
+    outbounds[i]->putData(inputSample);
+  }
+}
+
+/*display function*/
+void INode::display()const{
+  printf("INode id=%d type=%d ", id, type);
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*set the output to a ring buffer pointed to by buf, the output node will write output samples into this buf*/
+void ONode::setOutput(RingBuffer *buf){
+  output=buf;
+}
+
+/*each time onode executes, reading one sample from inbound FIFO and write this sample to ring buffer; also compute checksum*/
+void ONode::execute(){
+  int sampleOut=inbound->getData();
+  checksum ^= sampleOut;
+  output->insert(sampleOut);
+}
+
+/*display function*/
+void ONode::display()const{
+  printf("ONode id=%d type=%d %d\r\n", id, type, inbound->getId());
+}
+
+/*each time anode executes, reading operand1 from one inbound FIFO op1, reading operand2 from the other inbound FIFO op2,
+ compute the sum of the two and put this result onto all the outbound FIFOs*/
+void ANode::execute(){
+  int a = op1->getData(), b = op2->getData();
+  for(int i=0; i<(int)outbounds.size(); i++){
+    outbounds[i]->putData(a+b);
+  }
+}
+
+/*display function*/
+void ANode::display()const{
+  printf("ANode id=%d type=%d %d %d ", id, type, op1->getId(), op2->getId());
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*each time anode executes, reading operand1 from one inbound FIFO op1, reading operand2 from the other inbound FIFO op2,
+ compute the difference of the two and put this result onto all the outbound FIFOs*/
+void SNode::execute(){
+  int a = op1->getData(), b = op2->getData();
+  for(int i=0; i<(int)outbounds.size(); i++){
+    outbounds[i]->putData(a-b);
+  }
+}
+
+/*display function*/
+void SNode::display()const{
+  printf("SNode id=%d type=%d %d %d ", id, type, op1->getId(), op2->getId());
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*each time mnode executes, reading a sample from inbound FIFO, multiply it by c and divide by d, put the result onto all
+ the outbound FIFOs*/
+void MNode::execute(){
+  int a = inbound->getData();
+  for(int i=0; i<(int)outbounds.size(); i++){
+    outbounds[i]->putData(a*c/d);
+  }
+}
+
+/*display function*/
+void MNode::display()const{
+  printf("MNode id=%d type=%d c=%d d=%d %d ", id, type, c, d, inbound->getId());
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*each time dnode executes, read numOfSamples samples from inbound FIFO, put the first sample (sample 0) onto all the outbound FIFOs*/
+void DNode::execute(){
+  for(int i=0; i<numOfSamples; i++){
+    int d = inbound->getData();
+    if(i==0){
+      for(int j=0; j<(int)outbounds.size(); j++)outbounds[j]->putData(d);
+    }
+  }
+}
+
+/*display function*/
+void DNode::display()const{
+  printf("DNode id=%d type=%d n=%d %d ", id, type, numOfSamples, inbound->getId());
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*each time unode executes, read a sample from inbound FIFO, duplicate numOfCopies copies, put all those copies onto outbound FIFOs*/
+void UNode::execute(){
+  int a = inbound->getData();
+  for(int i=0; i<(int)outbounds.size(); i++){
+    for(int j=0; j<numOfCopies; j++){outbounds[i]->putData(a);}
+  }
+}
+
+/*display function*/
+void UNode::display()const{
+  printf("UNode id=%d type=%d n=%d %d ", id, type, numOfCopies, inbound->getId());
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*each time cnode executes, put the constant onto outbound FIFOs*/
+void CNode::execute(){
+  for(int i=0; i<outbounds.size(); i++){
+    outbounds[i]->putData(constant);
+  }
+}
+
+/*display function*/
+void CNode::display()const{
+  printf("CNode id=%d type=%d constant=%d ", id, type, constant);
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*each time fnode executes, read a sample from inbound FIFO, and put this sample onto outbound FIFOs*/
+void FNode::execute(){
+  int a = inbound->getData();
+  for(int i=0; i<(int)outbounds.size(); i++){
+    outbounds[i]->putData(a);
+  }
+}
+
+/*display function*/
+void FNode::display()const{
+  printf("FNode id=%d type=%d %d ", id, type, inbound->getId());
+  for(int i=0; i<outbounds.size(); i++)printf("%d ", outbounds[i]->getId());
+  printf("\r\n");
+}
+
+/*intended for the consumer to use, read one sample from the FIFO, and remove that data*/
+int FIFO::getData(){
+  if(this->fifo.empty()){
+    error("trying to read from empty FIFO: id=%d...\r\n", this->id);
+    exit(1);
+  }
+  int ret = this->fifo.front();
+  this->fifo.pop();
+  return ret;
+}
+
+/*intended for the producer to use, put one sample into the FIFO*/
+void FIFO::putData(int d){
+  this->fifo.push(d);
+}
+
+/*display function*/
+void FIFO::display()const{
+  printf("FIFO id=%d src=%d dst=%d numOfMarkings=%d\r\n", id, src->getId(), dst->getId(), fifo.size());
+}
+
+SDFG::SDFG(char* path, RingBuffer* input, RingBuffer* output):
+   gin(NULL),gout(NULL),numOfNodes(0),numOfFIFOs(0),scheduleTested(false),schedulable(false), scheduleFound(false)
+{
+    Parser::parseSDFG(path,this);
+    if(!hasSchedule()){
+        error("ERROR: CANNOT SCHEDULE\r\n");
+        exit(1);
+    }
+    setInput(input);
+    setOutput(output);
+    makeSchedule();
+}
+
+/*destructor, reclaim all the space allocated for nodes and FIFOs*/
+SDFG::~SDFG(){
+  for(int i=0; i<nodes.size(); i++)delete nodes[i];
+  for(int i=0; i<fifos.size(); i++)delete fifos[i];
+}
+
+/*add a node into the nodes vector, keep the nodes whose id's are arranged in ascending order in the nodes vector;
+  basically an insertion-sort*/
+void SDFG::addNode(SDFNode *node){
+  if(!node)return;
+  int i=0;
+  while(i<nodes.size()&&nodes[i]->getId()<node->getId())i++;
+  if(i<nodes.size()&&nodes[i]->getId()==node->getId())return;
+  else if(i>=nodes.size())nodes.push_back(node);
+  else{
+    nodes.push_back(NULL);
+    for(int j=nodes.size()-1;j>i;j--)nodes[j]=nodes[j-1];
+    nodes[i]=node;
+  }
+}
+
+/*given an id, if a FIFO with that id exists, return that FIFO; otherwise create a new FIFO, insert the FIFO into
+  vector fifos, and return that FIFO; also an insertion-sort implementation*/
+FIFO* SDFG::getFIFO(int id){
+  int i=0;
+  FIFO *fifo=NULL;
+  while(i<fifos.size()&&fifos[i]->getId()<id)i++;
+  if(i<fifos.size() && fifos[i]->getId()==id)return fifos[i];
+  else if(i>=fifos.size()){
+    fifo=new FIFO(id);
+    fifos.push_back(fifo);
+  }else{
+    fifo=new FIFO(id);
+    fifos.push_back(NULL);
+    for(int j=fifos.size()-1;j>i;j--)fifos[j]=fifos[j-1];
+    fifos[i]=fifo;
+  }
+  return fifo;
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with I, invoke this method to create a new INode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getINode(int nodeId, int params[], int count){
+  gin = new INode(nodeId,NULL);
+  addNode(gin);
+  for(int i=0;i<count;i++){
+    FIFO *fifo=getFIFO(params[i]);
+    gin->addOutbound(fifo);
+    fifo->setSrc(gin);
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with O, invoke this method to create a new ONode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getONode(int nodeId, int params[], int count){
+  gout = new ONode(nodeId,NULL);
+  addNode(gout);
+  for(int i=0;i<count;i++){
+    FIFO *fifo=getFIFO(params[i]);
+    gout->setInbound(fifo);
+    fifo->setDst(gout);
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with A, invoke this method to create a new ANode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getANode(int nodeId, int params[], int count){
+  ANode *anode = new ANode(nodeId);
+  addNode(anode);
+  FIFO *in1=NULL, *in2=NULL;
+  for(int i=0; i<count; i++){
+    if(i==0){
+      in1 = getFIFO(params[i]);
+      in1->setDst(anode);
+    }else if(i==1){
+      in2 = getFIFO(params[i]);
+      in2->setDst(anode);
+      anode->setInbound(in1, in2);
+    }else{
+      FIFO *out = getFIFO(params[i]);
+      out->setSrc(anode);
+      anode->addOutbound(out);
+    }
+  }
+}
+
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with S, invoke this method to create a new SNode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getSNode(int nodeId, int params[], int count){
+  SNode *snode = new SNode(nodeId);
+  addNode(snode);
+  FIFO *in1=NULL, *in2=NULL;
+  for(int i=0; i<count; i++){
+    if(i==0){
+      in1=getFIFO(params[i]);
+      in1->setDst(snode);
+    }else if(i==1){
+      in2=getFIFO(params[i]);
+      in2->setDst(snode);
+      snode->setInbound(in1,in2);
+    }else{
+      FIFO *out = getFIFO(params[i]);
+      out->setSrc(snode);
+      snode->addOutbound(out);
+    }
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with M, invoke this method to create a new MNode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getMNode(int nodeId, int params[], int count){
+  MNode *mnode = new MNode(nodeId, params[0], params[1]);
+  addNode(mnode);
+  for(int i=2; i<count; i++){
+    if(i==2){
+      FIFO *in=getFIFO(params[i]);
+      in->setDst(mnode);
+      mnode->setInbound(in);
+    }else{
+      FIFO *out=getFIFO(params[i]);
+      out->setSrc(mnode);
+      mnode->addOutbound(out);
+    }
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with D, invoke this method to create a new DNode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getDNode(int nodeId, int params[], int count){
+  DNode *dnode=new DNode(nodeId, params[0]);
+  addNode(dnode);
+  for(int i=1; i<count; i++){
+    if(i==1){
+      FIFO *in=getFIFO(params[i]);
+      in->setDst(dnode);
+      dnode->setInbound(in);
+    }else{
+      FIFO *out=getFIFO(params[i]);
+      out->setSrc(dnode);
+      dnode->addOutbound(out);
+    }
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with U, invoke this method to create a new UNode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getUNode(int nodeId, int params[], int count){
+  UNode *unode = new UNode(nodeId, params[0]);
+  addNode(unode);
+  for(int i=1; i<count; i++){
+    if(i==1){
+      FIFO *in=getFIFO(params[i]);
+      in->setDst(unode);
+      unode->setInbound(in);
+    }else{
+      FIFO *out=getFIFO(params[i]);
+      out->setSrc(unode);
+      unode->addOutbound(out);
+    }
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with C, invoke this method to create a new CNode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getCNode(int nodeId, int params[], int count){
+  CNode *cnode=new CNode(nodeId, params[0]);
+  addNode(cnode);
+  for(int i=1; i<count; i++){
+    FIFO *out=getFIFO(params[i]);
+    out->setSrc(cnode);
+    cnode->addOutbound(out);
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with F, invoke this method to create a new FNode; refer
+  to the document regarding what each element in params array means*/
+void SDFG::getFNode(int nodeId, int params[], int count){
+  FNode *fnode=new FNode(nodeId);
+  addNode(fnode);
+  for(int i=0; i<count; i++){
+    if(i==0){
+      FIFO *in=getFIFO(params[i]);
+      in->setDst(fnode);
+      fnode->setInbound(in);
+    }else{
+      FIFO *out=getFIFO(params[i]);
+      out->setSrc(fnode);
+      fnode->addOutbound(out);
+    }
+  }
+}
+
+/*invoked when parsing sdfgconf.txt. When reading a line starting with E, invoke this method to add delay to a specific edge,
+  namely put initial data into the FIFO associated with the edge; refer to the document regarding what each element in params array means*/
+void SDFG::addDelay(int params[], int count){
+  FIFO *fifo=getFIFOAt(params[0]);
+  if(!fifo){
+    error("trying to add delay to a nonexisting edge\r\n");
+    exit(1);
+  }
+  for(int i=0; i<params[1]; i++){
+    fifo->putData(params[i+2]);
+  }
+}
+
+/*Each row in topology matrix corresponds to a FIFO; on a row-by-row basis, if node is the producer (src) of a FIFO, then set
+  tokenProduced to be the number of tokens the producer produces; the same for consumer; the number of tokens produced or consumed
+  can be found in the specification*/
+void SDFG::buildTopologyMatrix(){
+  SDFNode *src=NULL, *dst=NULL;
+  int tokenProduced=0, tokenConsumed=0;
+  for(int i=0; i<fifos.size(); i++){
+    tokenConsumed=tokenProduced=1;
+    src=fifos[i]->getSrc();
+    dst=fifos[i]->getDst();
+    if(src->getType()==U){
+      UNode *unode=(UNode*) src;
+      tokenProduced=unode->getNumOfCopies();
+    }
+    if(dst->getType()==D){
+      DNode *dnode=(DNode*) dst;
+      tokenConsumed=dnode->getNumOfSamples();
+    }
+    topologyMatrix[i].setValue(src->getId(), tokenProduced, dst->getId(), tokenConsumed);
+  }
+}
+
+/*print the full topology matrix*/
+void SDFG::printTopologyMatrix()const{
+  for(int i=0; i<fifos.size(); i++){
+    for(int j=0; j<nodes.size(); j++){
+      if(topologyMatrix[i].getSrc()==nodes[j]->getId())printf("%d ", topologyMatrix[i].getTokensProduced());
+      else if(topologyMatrix[i].getDst()==nodes[j]->getId())printf("%d ", 0-topologyMatrix[i].getTokensConsumed());
+      else printf("0 ");
+    }
+    printf("\r\n");
+  }
+}
+
+/*test if the SDF has a schedule by solving the equation group; RationalNum is a class used to represent rational numbers,
+  useful in producing integer solutions to equation groups*/
+bool SDFG::hasSchedule(){
+  /*if this function has been called before, just return the recorded result*/
+  if(scheduleTested)return schedulable;
+  /*solution to the equation group, at most 256 variables, each variable corresponds to a node in SDF*/
+  vector<RationalNum> solution(256);
+  /*count keeps track of the number of variables in the equation group whose value has been found*/
+  int count=0;
+  /*initial values for the variables, initialize them to be -1 to flag that their values have not been found*/
+  for(int i=0; i<256; i++)solution[i]=RationalNum(-1);
+  /*set the value of first variable to 1, this is OK since the solution to the equation group is not unique*/
+  solution[0]=RationalNum(1);
+  count++;
+  /*repeat for each equation until the values of all variables have been found*/
+  do{
+    for(int i=0; i<numOfFIFOs; i++){
+      /*node1 and node2 correspond to the src and dst of the FIFO, we use them to index into the solution vector*/
+      int node1,node2;
+      node1=topologyMatrix[i].getSrc();
+      node2=topologyMatrix[i].getDst();
+      /*we have found the values for neither of them, there is nothing more we can do for now, so just skip and proceed*/
+      if(solution[node1]<0&&solution[node2]<0)continue;
+      /*we have found the values for both of them, then we plug in their values into this equation to see if the solution
+        satisfies this equation*/
+      else if(solution[node1]>=0&&solution[node2]>=0){
+        /*values which we already found do not satisfy this current equation, meaning there is conflict, namely there is no
+          schedule for this SDF*/
+        if(solution[node1]*topologyMatrix[i].getTokensProduced()!=solution[node2]*topologyMatrix[i].getTokensConsumed()){
+          scheduleTested=true;
+          schedulable=false;
+          return false;
+        }
+      }
+      /*node1's value is not found, but node2's value is found, then use node2 to solve node1*/
+      else if(solution[node1]<0 && solution[node2]>=0){
+        RationalNum r(topologyMatrix[i].getTokensConsumed(), topologyMatrix[i].getTokensProduced());
+        solution[node1]=r*solution[node2];
+        /*we have found the value for one more variable, thus we should update count*/
+        count++;
+      }
+      /*node2's value is not found, but node1's value is found, then use node1 to solve node2*/
+      else if(solution[node1]>=0 && solution[node2]<0){
+        RationalNum r(topologyMatrix[i].getTokensProduced(), topologyMatrix[i].getTokensConsumed());
+        solution[node2]=r*solution[node1];
+        /*we have found the value for one more variable, thus we should update count*/
+        count++;
+      }
+    }
+  }while(count<numOfNodes);
+  /*adding all the elements in solution vector together; perform this process simply to make their denominators the same*/
+  RationalNum temp(0);
+  for(int i=0; i<numOfNodes; i++)temp=temp+solution[i];
+  /*for each element in solution, multiply by the common denominator found just now; in this way, we can acquire a set of integer
+    solution to the original equations group, this solution represents how many times a node has to run in a schedule, if there
+    exists one; then store the values in numOfFiringRequired vector*/
+  for(int i=0; i<numOfNodes; i++){
+    solution[i]=solution[i]*temp.getDenominator();
+    numOfFiringRequired.push_back(solution[i].getNumerator());
+  }
+  /*if we are here, we have found a schedule*/
+  scheduleTested=true;
+  schedulable=true;
+  return true;
+}
+
+/*print number of times each node is required to fire in one schedule*/
+void SDFG::printNumOfFiringRequired()const{
+  for(int i=0; i<numOfFiringRequired.size(); i++){
+    printf("node %d will fire %d time(s)\r\n", i, numOfFiringRequired[i]);
+  }
+}
+
+/*compile a schedule according to numOfFiringRequired; each time find a node which can fire and fire it; update the tokens on each FIFO,
+  repeat this process until all the nodes have fired the required number of times, which means a complete schedule has finished*/
+void SDFG::makeSchedule(){
+  vector<int> buffers(fifos.size()), firingCount(nodes.size());
+  for(int i=0; i<buffers.size(); i++)buffers[i]=fifos[i]->getSize();
+  for(int i=0; i<firingCount.size(); i++)firingCount[i]=0;
+  bool finished;
+  do{
+      /*check if all nodes have fired the number of times required, as computed in hasSchedule() function*/
+      finished=true;
+      for(int i=0; i<firingCount.size(); i++){
+        if(firingCount[i]<numOfFiringRequired[i]){
+          /*this node has not fired that many times, then it has not finished*/
+          finished=false;
+          vector<int> tempBuf(buffers);
+          bool valid=true;
+          /*test if this node can fire, namely if it has all its input data ready on the inbound FIFOs; we are doing matrix multiplication
+            with a vector in a compressed form, if the resulting buffer only has non-negative elements, then it means this node can
+            fire*/
+          for(int j=0; j<tempBuf.size(); j++){
+            if(topologyMatrix[j].getSrc()==i){
+              tempBuf[j]+=topologyMatrix[j].getTokensProduced();
+            }else if(topologyMatrix[j].getDst()==i){
+              tempBuf[j]-=topologyMatrix[j].getTokensConsumed();
+              if(tempBuf[j]<0){
+                valid=false;
+                break;
+              }
+            }
+          }
+          if(valid){
+            /*fire this node*/
+            firingCount[i]++;
+            schedule.push_back(i);
+            for(int j=0; j<tempBuf.size(); j++)buffers[j]=tempBuf[j];
+          }
+        }
+      }
+  }while(!finished);
+}
+
+/*display schedule*/
+void SDFG::printSchedule()const{
+  for(int i=0; i<schedule.size(); i++){
+    printf("fire node %d ", schedule[i]);
+    nodes[schedule[i]]->display();
+  }
+}
+
+/*set the input of the SDF to a ring buffer buf*/
+void SDFG::setInput(RingBuffer *buf){
+  gin->setInput(buf);
+}
+
+/*set the output of the SDF to a ring buffer buf*/
+void SDFG::setOutput(RingBuffer *buf){
+  gout->setOutput(buf);
+}
+
+/*execute the SDF according to the computed schedule, firing each node for required number of times, repeat this process for
+  numOfRepetitions iterations*/
+void SDFG::execute(int numOfRepetitions){
+  for(int i=0; i<numOfRepetitions; i++){
+    for(int j=0; j<schedule.size(); j++){
+      getNodeAt(schedule[j])->execute();
+    }
+  }
+}
\ No newline at end of file