#include <stdio.h>
#include <math.h>

#include "path.h"

#include "macro.H"
#include "techno.H"

#include "params.h"

pathlistt pathlist;

// delay of n-bit on-chip RAM, A addresses,
// ro_ports read-only ports, wo_ports write only ports

long dram(int A, int n, int ro_ports, int wo_ports)
 {
  return (long)ceil(dram(A, n)*(0.5+0.5*(2*wo_ports+ro_ports)/2));
 }

// delay of n-bit on-chip register based RAM, A addresses,
// ro_ports read-only ports, wo_ports write only ports

long d_reg_ram(int A, int n, int ro_ports, int wo_ports)
 {
  return ddecf(ld(A))+ddriv;
 }

// **************************************
// **            DATA PATHS            **
// **************************************

// external signals

patht pup("pup");

// **************************************
// **           ENVIRONMENTS           **
// **************************************

// delay of the opgen environment

long opgen()
 {
  return 5*dor+         // AOD; log numax=5
         dinv+4*dand+   // ACM; log lmax=4
         1*dor+dand;    // DCN; log faninmax=1
 }

// delay of the ALU
// with conditional sum adder
// The ALU would be on the critical path with a CLA

long alu()
 {
  return dxor+dCSA(32)+dand+dor+dor+2*dmux;
 }

void environments()
 {
  // Special purpose registers

  patht EPC("EPC"), EPCn("EPCn"), SR("SR");

  // PC environment part one

  patht new_pc1;
  patht pc0("pc0"), pc1("pc1"), opc1("opc1");

  new_pc1=pc0+dincf(30)+"Incrementer"+"new_pc1";
  pathlist.add(new_pc1+dff+delta+"pc1");

  // instruction memory environment

  patht ir0, pff0, ibusy;

  // really fast memory is assumed

  pathlist.add(pc0+"IM.Adr");

  pff0+="IM.pff0"+0;
  ibusy+="IM.Ibusy"+0;
  ir0+="IM.Dout"+0;

  ir0=max(ir0, max(pup, max(pff0, ibusy)+dor)+dor+dinv)+dand;

  // Decode / issue environment

  patht ir1("DECODE.IR1"), pff1("DECODE.PFF1");

  patht decode_ir=max(ir1, pup)+dmux;

  patht op=decode_ir+opgen()+"opgen";

  patht D_bus=op+"D-Bus";

  patht FU=op+"FU";

  patht co1=max(decode_ir, op)+dmux+"co1gen";

  // sourcereg is the information about the source operands generated by Agen
  patht sourcereg=max(op, decode_ir)+max(2*dmux, dor)+"Agen/sourcereg";

  // ROB

  patht ROB_head("ROB.head"), ROB_tail("ROB.tail"), ROB_count("ROB.count");

  patht ROB_A7=ROB_head+"ROB.A7";
  patht wb_ROB1_Dout=ROB_A7+
          (trick2?d_reg_ram(ROB_SIZE, 105, trick1?4:7, 2):dram(ROB_SIZE, 105, trick1?4:7, 2))+
          "ROB1 RAM";

  patht CA=wb_ROB1_Dout+"CA";
  patht MCA=max(SR, CA)+cand+"MCA";

  patht JISR=MCA+dtree(32)*dor+"JISR";

  // CDB Control

  patht FUi_CDBack=pup+dmux+"FUi.CDBack";

  // Reservation stations

  patht CDBoe=FUi_CDBack+"CDBoe";

  patht CDB=CDBoe+ddriv+"CDB";

  // FU

  patht FUstall=FUi_CDBack+dinv+dand+"FUstall";
  patht RSi_full("RSi.full");

  // Decode / issue environment 

  patht destreg=max(decode_ir, op)+5*dmux+"destreg";

  patht Prod_opx=sourcereg+"Prod.opx";
  patht Prod_opx_Dout=Prod_opx+d_reg_ram(32, 5, 2, 2)+"Prod.opx.Dout";

  patht RF_opx=sourcereg+"RF.opx";
  patht RF_opx_Dout=RF_opx+dram(32, 32, 2, 1)+"RF.opx.Dout";

  patht ROB_Ax=Prod_opx_Dout+"ROB.Ax";
  patht opx_ROB_Dout=ROB_Ax+
          (trick2?d_reg_ram(32, 105, trick1?4:7, 2):dram(32, 105, trick1?4:7, 2))+
          "ROB1 RAM"+"opx.ROB.Dout";

  patht operands=max(sourcereg+dor+cinv, co1, 
                max(Prod_opx_Dout, RF_opx_Dout, 
                max(CDB, 
                max(CDB, Prod_opx_Dout)+dand+dEQ(TAG_WIDTH),
                opx_ROB_Dout+dmux+dmux))+dmux)+dmux+"operands";

  patht bstall=operands+dinv+"bstall";

  patht jumpR=op+"jumpR";

  patht FU_full=max(FUstall, RSi_full)+dtree(3)*cand+"FUi.full";

  patht FUbusy=max(FU_full, FU)+dtree(FUS)*dor+dand+"FUbusy";

  patht ROB_full=ROB_count+dEQ(6)+"ROB.full";
  patht ROB_empty=ROB_count+dzero(6)+"ROB.empty";

  patht issuestall=max(
      max(FUbusy, ROB_full, bstall),
      max(op+dand, ROB_empty+dinv),
    JISR+dinv)+"issuestall";

  patht ir1ce=issuestall+cinv+"ir1ce";

  pathlist.add(max(ir0, ir1ce)+dff+delta+"IR1");
  pathlist.add(max(pff0, ir1ce)+dff+delta+"PFF1");

  patht Prod_op4=max(
    max(issuestall+dinv, destreg+dinv)+dand+dor,
    ROB_tail)+"Prod.op4";

  pathlist.add(max(ROB_tail, destreg)+"Prod.op4.Din"+
               d_reg_ram(32, 5, 2, 2)+"Prod.op4");

  patht AEQZ=operands+dzero(32)+"AEQZ";

  patht bjtaken=max(AEQZ, decode_ir)+dinv+dor+dand+"bjtaken";

  patht rfeEPCn("rfeEPCn");

  pathlist.add(max(op, issuestall+cinv+"rfece")+dff+delta+"rfeEPCn");

  // PC environment part two

  patht target1;
  patht pc_tmp_path, new_pc0, JISRrfe, setPC;

  patht DOrfe=max(op, ROB_empty)+dand;

  JISRrfe=max(JISR, DOrfe)+dor+"JISRrfe";
  setPC=max(JISR, DOrfe, op)+dtree(5)*dor+"setPC";

  pc_tmp_path=max(pc1+dCLA(32)+"Adder", operands, jumpR)+dmux;
  target1=max(bjtaken, new_pc1, pc_tmp_path)+dmux+"target1";
  new_pc0=max(new_pc1, max(pc_tmp_path, 
              max(JISR, 
              max(EPC, EPCn, rfeEPCn)+dmux)+dmux, 
              JISRrfe)+dmux, setPC)+dmux+"new_pc0";

  pathlist.add(new_pc0+dff+delta+"pc0");

  // Reservation stations

  pathlist.add(max(D_bus+dmux, D_bus+dor)+dff+delta+"RS.ctlreg");

  pathlist.add(max(D_bus+dinv+dand+dmux, 
    CDB+dor+dand+dand+dmux+dEQ(TAG_WIDTH), D_bus+dor)+dff+delta+"RS.operand.ctlreg");

  patht RSi_valid=CDB+dtree(7)*dand+dor+dand+dEQ(TAG_WIDTH)+"RSi.valid";

  patht RSi_doe=RSi_valid+dFL1u(2)+dand+"RSi.doe";

  patht RSi_fill=RSi_doe+dor+dPP(2)*dor+"RSi.fill";

  pathlist.add(RSi_fill+dor+"RS.ctlregce"+dff+delta+"RS.ctlreg");

  patht FUdata=max(RSi_valid, RSi_doe)+ddriv+dmux;

  pathlist.add(FUdata+alu()+"ALU"+dff+delta+"Producer");

  patht FUi_CDBreq=max(FUdata, FUi_CDBack+dand+dinv)+dor;

  // Data memory reservation stations

  // paths identlical to the ordinary reservation stations are skipped

  pathlist.add(CDB+"Data memory address"+dCSA(32)+dmux+dff+delta+"RS.operand.ctlreg");

  patht FUdata_mem=max(FUdata, CDB+"Data memory address"+dCSA(32)+dmux);

  pathlist.add(FUdata_mem+dmux+"Single adjust"+
               ddecf(3)+3*dand+dor+"MwGen"+
               3*dmux+dand+"Align4S"+dff+delta+"M");

  // Decode / issue environment

  patht ROB_op8=max(
    max(issuestall+dinv, ROB_tail, co1),
    max(op, destreg, target1),
    opc1+dor)+"ROB.op8";

  // CDB control

  pathlist.add(
    max(
        max(FUi_CDBack+dPP(PRODUCERS)*dor+dinv, FUi_CDBreq)+
        dand+dFL1u(2*PRODUCERS)+dor,
        max(pup, JISR)+dinv+dor
       )+dand+"new_FUi.CDBack"+
       dff+delta+"FUi.CDBack");

  // ROB

  patht ROB_retire=max(ROB_empty+dinv, wb_ROB1_Dout)+dand;

  patht ROB_tailce=max(JISR, issuestall+dinv)+dor+"ROB.tailce";
  patht ROB_headce=max(JISR, ROB_retire)+dor+"ROB.headce";
  patht ROB_countce=max(JISR, ROB_retire, issuestall+dinv)+2*dor+"ROB.countce";

  pathlist.add(max(JISR+dinv, ROB_head+dincf(4))+dand+"new_ROB.head"+dff+delta+"ROB.head");
  pathlist.add(max(JISR+dinv, ROB_tail+dincf(4))+dand+"new_ROB.tail"+dff+delta+"ROB.tail");

  pathlist.add(max(JISR+dinv, max(ROB_count, ROB_retire)+dxor+dincf(6)+dxor)+dand+dff+delta);

  patht writeback=max(JISR+dinv, MCA+dtree(5)*dor+dinv)+dor+"writeback";

  pathlist.add(max(wb_ROB1_Dout, ROB_retire, writeback)+"RF.op3.Din"+
               dff+delta+"RF.op3");

  patht Prod_op3_A=wb_ROB1_Dout+"Prod.op3.A";
  patht Prod_op3_Dout=Prod_op3_A+d_reg_ram(32, 5, 2, 1)+"Prod.op3.Dout";

  pathlist.add(max(max(wb_ROB1_Dout, ROB_retire, writeback),
                   max(Prod_op3_Dout, ROB_head)+dEQ(TAG_WIDTH))+dand+"Prod.op3.Din"+
                   dff+delta+"Prod.op3");
 }

void parse_args(int argc, char **argv)
 {
  int i;

  for(i=1; i<argc; i++)
   {
    if(checkparam(argv[i]))
     {
      fprintf(stderr, "Unknown option %s\n", argv[i]);
      exit(1);
     }
   }
 }

int main(int argc, char **argv)
 {
  parse_args(argc, argv);

  init_techno(MOTOROLA);

  environments();

  printf("Longest Path:\n");

  pathlist.max().print();

  return 0;
 }
