def getEntityDeclaration(stateMachine):
  tw = text.TextWriter(4, "--")

  entityName = "hw_core_" + stateMachine.name()

  # Write library using statements.
  tw.writeLine("library IEEE;")
  tw.writeLine("use IEEE.STD_LOGIC_1164.ALL;")
  tw.writeLine("use IEEE.NUMERIC_STD.ALL;")

  tw.writeBlankLine()

  # Write the entity declaration.
  tw.writeLine("entity " + entityName + " is")

  # Write port definitions.
  tw.increaseIndent()
  tw.writeLine("port (")
  tw.increaseIndent()

  ports = getPorts(stateMachine)
  for port in ports[:-1]:
    tw.writeLine(port[0] + " : " + port[1] + " " + port[2] + ";")

  tw.writeLine(ports[-1][0] + " : " + ports[-1][1] + " " + ports[-1][2])

  tw.decreaseIndent()
  tw.writeLine(");")
  tw.decreaseIndent()
  tw.writeLine("end " + entityName + ";")

  return str(tw)
def getControllerStateMachinesDone(stateMachines):
  tw = text.TextWriter(4, "--")

  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()

  for sm in stateMachines:
    tw.writeLine("if " + sm.name() + "_done = '1' then")
    tw.increaseIndent()
    tw.writeLine("int_state <= S_DONE;")
    tw.writeLine("wakeup <= \"11\";")
    tw.writeBlankLine()
    tw.writeCommentLine("Update carry flag in MSR register.")
    tw.writeLine("int_msr(2) <= carry_out;")
    tw.decreaseIndent()
    tw.writeLine("end if;")

  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()

  return str(tw)
def reportAcceleratorStart(stateMachines):
  tw = text.TextWriter(4, "--")

  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()

  for sm in stateMachines:
    tw.writeLine("if " + sm.name() + "_sel = '1' and " + sm.name() + "_done = '0' and accel_started = '0' then")
    tw.increaseIndent()
    tw.writeLine("report \"TESTBENCH: " + sm.name() + " EXECUTION START.\";")
    tw.writeLine("accel_started <= '1';")
    tw.writeLine("core_start <= cycles;")
    tw.decreaseIndent()
    tw.writeLine("elsif " + sm.name() + "_done = '1' and accel_started = '1' then")
    tw.increaseIndent()
    tw.writeLine("core_cycles := core_cycles + (cycles - core_start);")
    tw.writeLine("report \"TESTBENCH: " + sm.name() + " EXECUTION COMPLETE: \" & Integer'image(cycles - core_start) & \" CYCLES.\";")
    tw.writeLine("accel_started <= '0';")
    tw.writeLine("overhead_start <= cycles;")
    tw.decreaseIndent()
    tw.writeLine("end if;")
    tw.writeBlankLine()

  return str(tw)
def getComponentDefinition(stateMachine):
  # Write component statement.
  tw = text.TextWriter(4, "--")
  tw.increaseIndent()

  entityName = "hw_core_" + stateMachine.name()
  tw.writeLine("component " + entityName + " is")

  # Write port definitions.
  tw.increaseIndent()
  tw.writeLine("port (")
  tw.increaseIndent()

  ports = getPorts(stateMachine)
  for port in ports[:-1]:
    tw.writeLine(port[0] + " : " + port[1] + " " + port[2] + ";")

  tw.writeLine(ports[-1][0] + " : " + ports[-1][1] + " " + ports[-1][2])

  tw.decreaseIndent()
  tw.writeLine(");")

  tw.decreaseIndent()
  tw.writeLine("end component " + entityName + ";")
  tw.decreaseIndent()
  tw.writeBlankLine()

  return str(tw)
def getControllerWriteRegisters(stateMachines):
  tw = text.TextWriter(4, "--")
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()

  tw.writeLine("when x\"44A00000\" =>")
  tw.increaseIndent()

  tw.writeBlankLine()

  tw.writeLine("case M_AXI_DP_0_wdata is")
  for sm in stateMachines:
    tw.writeCommentLine("Select accelerator " + sm.name() + ".")
    tw.writeLine("when x\"{:08x}\" =>".format(sm.id()))
    tw.increaseIndent()
    tw.writeLine(sm.name() + "_sel <= '1';")
    tw.writeLine("int_state <= S_WAITING_FOR_CORE;")
    tw.writeBlankLine()
    tw.decreaseIndent()

  tw.writeCommentLine("A non-existent ID is undefined behaviour.")
  tw.writeLine("when others => null;")
  tw.writeBlankLine()
  tw.writeLine("end case;")
  tw.writeBlankLine()
  tw.decreaseIndent()

  for i in range(1,31):
    tw.writeLine("when x\"44A0{:04x}\" =>".format((i)*4))
    tw.increaseIndent()
    tw.writeLine("reg_to_accel_{:02d} <= M_AXI_DP_0_wdata;".format(i))
    tw.decreaseIndent()

  tw.writeCommentLine("Register r31 is unused by program, therefore it is safe to assume it is never an input to a core.")
  tw.writeLine("when x\"44A0{:04x}\" =>".format(31*4))
  tw.increaseIndent()
  tw.writeCommentLine("Carry flag is bit 2 of MSR.")
  tw.writeLine("carry_in <= M_AXI_DP_0_wdata(2);")
  tw.writeLine("int_msr <= M_AXI_DP_0_wdata;")
  tw.decreaseIndent()

  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()

  return str(tw)
def getTestbenchSignals(stateMachine):
  tw = text.TextWriter(4, "--")

  tw.increaseIndent()

  for port in getPorts(stateMachine):
    if port[0] == "rst" or port[0] == "done" or port[0] == "sel":
      tw.writeLine("signal " + stateMachine.name() + "_" + port[0] + " : " + port[2] + ";")

  tw.writeBlankLine()
  tw.decreaseIndent()

  return str(tw)
def getControllerUnreset(stateMachines):
  tw = text.TextWriter(4, "--")

  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()

  for sm in stateMachines:
    tw.writeLine(sm.name() + "_rst <= '0';")

  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()

  return str(tw)
def getUUTDefinition(stateMachine):
  tw = text.TextWriter(4, "--")

  tw.increaseIndent()
  entityName = "hw_core_" + stateMachine.name()
  tw.writeLine(stateMachine.name() + "_uut : " + entityName + " port map")
  tw.writeLine("(")

  tw.increaseIndent()

  ports = getPorts(stateMachine)
  for port in ports[:-1]:
    actualName = port[3]
    tw.writeLine(port[0] + " => " + actualName + ",")

  tw.writeLine(ports[-1][0] + " => " + ports[-1][3])

  tw.decreaseIndent()
  tw.writeLine(");")
  tw.writeBlankLine()

  return str(tw)
def getControllerReadRegisters(stateMachines):
  tw = text.TextWriter(4, "--")
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()
  tw.increaseIndent()

  tw.writeLine("when x\"44A00000\" =>")
  tw.increaseIndent()
  tw.writeBlankLine()
  tw.writeCommentLine("Reset all state machines.")
  for sm in stateMachines:
    tw.writeLine(sm.name() + "_rst <= '1';")
    tw.writeLine(sm.name() + "_sel <= '0';")

  tw.writeBlankLine()
  tw.writeCommentLine("Send carry signal to MicroBlaze core.")
  tw.writeLine("M_AXI_DP_0_rdata <= int_msr;")
  tw.writeCommentLine("Put controller into READY state.")
  tw.writeLine("int_state <= S_READY;")
  tw.decreaseIndent()

  for i in range(1,31):
    tw.writeLine("when x\"44A0{:04x}\" =>".format((i)*4))
    tw.increaseIndent()
    tw.writeLine("M_AXI_DP_0_rdata <= reg_from_accel_{:02d};".format(i))
    tw.decreaseIndent()

  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()
  tw.decreaseIndent()

  return str(tw)
def getArchitecturalDefinition(stateMachine):
  tw = text.TextWriter(4, "--")

  entityName = "hw_core_" + stateMachine.name()

  # Write architectural definition.
  tw.writeLine("architecture " + entityName + "_behav of " + entityName + " is")

  # Write type definition of STATE type.
  tw.increaseIndent()
  tw.writeLine("type STATE is (")
  tw.increaseIndent()
  tw.writeLine("S_RESET,")

  for i in range(len(stateMachine)-1):
    tw.writeLine(stateMachine[i].name() + ",")

  tw.writeLine(stateMachine[len(stateMachine)-1].name())
  tw.decreaseIndent()
  tw.writeLine(");")

  tw.writeBlankLine()

  # Declaration of internal state register.
  tw.writeLine("signal int_state    : STATE;")
  tw.writeBlankLine()

  # Declaration of internal registers.
  for r in stateMachine.usedRegisters():
    if r != 0:
      tw.writeLine("signal " + "r{:02d}".format(r) + "          : unsigned(31 downto 0);")

  # Constant r0, which is a hardwired 0 value.
  tw.writeLine("constant r00          : unsigned(31 downto 0) := x\"00000000\";")

  # Output the 'offset' signal. This is used for calculating offsets for IO of bytes and halfwords.
  tw.writeCommentLine("Offset.")
  tw.writeLine("signal offset         : Integer range 0 to 31;")
  tw.writeBlankLine()

  # Carry signal. This is used in addition and subtraction operations.
  tw.writeCommentLine("Carry.")
  tw.writeLine("signal carry          : std_logic;")
  tw.writeBlankLine()

  # Begin behavioural definition.
  tw.decreaseIndent()
  tw.writeBlankLine()
  tw.writeLine("begin")

  # Begin process.
  tw.increaseIndent()
  tw.writeLine("process(clk, rst)")
  tw.increaseIndent()

  # Output local variables for each state.
  # These are 'namespaced' by state name so they don't conflict.
  for i in range(len(stateMachine)):
    if len(stateMachine[i].locals()) > 0:
      tw.writeCommentLine("State "
                          + stateMachine[i].name()
                          + " Locals: "
                          + ", ".join(list(map(lambda r: "r{:02d}".format(r), stateMachine[i].locals())))
                          + ".")

    for local in stateMachine[i].locals():
      if local != 0:
        tw.writeLine("variable " + localName(stateMachine[i].name(), local) + "   : unsigned(31 downto 0);")

    if len(stateMachine[i].locals()) > 0:
      tw.writeBlankLine()

  # Output the 'temp64' variable. This is used by operations which produce a 64-bit result (e.g. multiply)
  tw.writeCommentLine("Temporary 64-bit variable.")
  tw.writeLine("variable temp64      : unsigned(63 downto 0);")
  tw.writeBlankLine()

  # Output the 'temp33' variable. This is used by operations which can overflow.
  tw.writeCommentLine("Temporary 33-bit variable.")
  tw.writeLine("variable temp33      : unsigned(32 downto 0);")
  tw.writeBlankLine()

  tw.writeCommentLine("Temporary 32-bit variable.")
  tw.writeLine("variable temp32      : unsigned(31 downto 0);")

  # Output the 'temp_carry' variable. This is used to store the carry flag during computations.
  tw.writeCommentLine("Temporary carry flag.")
  tw.writeLine("variable temp_carry  : std_logic;")
  tw.writeBlankLine()

  # Start the process body.
  tw.decreaseIndent()
  tw.writeLine("begin")
  tw.increaseIndent()

  # Reset condition. If rst = 1 then reset internal values.
  tw.writeLine("if rst = '1' then")
  tw.increaseIndent()
  tw.writeLine("int_state  <= S_RESET;")
  tw.writeLine("m_data_out <= (others => '0');")
  tw.writeLine("m_addr     <= (others => '0');")

  tw.writeBlankLine()

  for out in stateMachine.outputRegisters():
    tw.writeLine("out_r{:02d} <= (others => '0');".format(out))

  tw.writeBlankLine()

  tw.writeLine("m_rd       <= '0';")
  tw.writeLine("m_wr       <= \"0000\";")
  tw.writeLine("done       <= '0';")
  tw.writeLine("carry_out  <= '0';")

  tw.decreaseIndent()

  tw.writeLine("elsif sel = '1' then")
  tw.increaseIndent()

  # Rising clock edge condition.
  # Transition to next state for all states that have a clock transition.
  tw.writeLine("if rising_edge(clk) then")
  tw.increaseIndent()

  tw.writeBlankLine()

  tw.writeLine("case int_state is")

  # Reset state condition.
  tw.writeLine("when S_RESET =>")
  tw.increaseIndent()
  tw.writeLine("int_state <= " + stateMachine[0].name() + ";")
  tw.decreaseIndent()

  for i in range(len(stateMachine)):
    s = stateMachine[i]

    tw.writeBlankLine()
    tw.writeLine("when " + s.name() + " =>")
    tw.increaseIndent()

    if "M_RDY" in s.triggers():
      tw.writeLine("if m_rdy = '1' then")
      tw.increaseIndent()

      # If the wait is a READ, we need to get the data from memory into the correct register.
      if s.instruction().isRead():
        inst = s.instruction()

        tw.writeCommentLine(str(inst))
        tw.writeCommentLine("Read data into r{:02d}.".format(inst.rD()))

        if inst.width() == 1:
          tw.writeCommentLine("Loading byte.")
          tw.writeLine("r{:02d} <= (others => '0');".format(inst.rD()))
          tw.writeLine("r{:02d}(7 downto 0) <= unsigned(m_data_in((offset+7) downto offset));".format(inst.rD()))
        elif inst.width() == 2:
          tw.writeCommentLine("Loading halfword.")
          tw.writeLine("r{:02d} <= (others => '0');".format(inst.rD()))
          tw.writeLine("r{:02d}(15 downto 0) <= unsigned(m_data_in((offset+15) downto offset));".format(inst.rD()))
        elif inst.width() == 4:
          tw.writeLine("r{:02d}".format(inst.rD()) + " <= unsigned(m_data_in);")
        else:
          raise ValueError("Invalid width " + str(inst.width()) + " while translating instruction " + str(inst) + ".")

        tw.writeBlankLine()

      tw.writeCommentLine("Transition to " + s.getTransition("M_RDY").name() + ".")
      tw.writeLine("int_state <= " + s.getTransition("M_RDY").name() + ";")

      tw.decreaseIndent()
      tw.writeLine("end if;")

    # We transition to the next state if this is not a wait state.
    elif "CLK" in s.triggers() and s.getTransition("CLK") != s:
      tw.writeCommentLine("Transition to " + s.getTransition("CLK").name() + ".")
      tw.writeLine("int_state <= " + s.getTransition("CLK").name() + ";")


    else:
      tw.writeCommentLine("State with no transitions.")
      tw.writeLine("null;")

    tw.decreaseIndent()

  tw.writeLine("end case;")

  tw.writeBlankLine()
  tw.writeCommentLine("Perform an operation depending on the state we're in.")
  tw.writeLine("case int_state is")

  # Other states.
  for i in range(len(stateMachine)):
    s = stateMachine[i]

    tw.writeLine("when " + s.name() + " =>")
    tw.increaseIndent()

    # If this is a start state, get inputs into the state machine's internal registers.
    if s.isStartState():
      tw.writeCommentLine("Start state. Read inputs into internal registers.")
      for r in stateMachine.inputRegisters():
        tw.writeLine("r{reg:02d} <= unsigned(in_r{reg:02d});".format(reg=r))
      tw.writeBlankLine()
      tw.writeCommentLine("Get carry flag input.")
      tw.writeLine("carry <= carry_in;")

    # If this is an end state, put outputs from the state machine's internal registers.
    # Also set the done flag.
    elif s.isEndState():
      tw.writeCommentLine("End state. Write internal registers to outputs.")
      for r in stateMachine.outputRegisters():
        tw.writeLine("out_r{reg:02d} <= std_logic_vector(r{reg:02d});".format(reg=r))
      tw.writeLine("done <= '1';")
      tw.writeLine("carry_out <= carry;")
      tw.writeBlankLine()

    # If this is a wait state, set up the memory transaction (either a read or a write).
    elif s.isWaitState():
      tw.writeCommentLine("Wait state. Set up a memory transaction.")
      inst = s.instruction()

      # Figure out what the expression is for the memory address.
      # It's either rA+imm or rA+rB.
      if inst.imm() != None:
        expr = "(r{reg:02d} + unsigned(to_signed({imm:d}, 32)))".format(reg = inst.rA(), imm = inst.imm())
      elif inst.label() != None:
        label = "%%SYM_" + inst.label() + "%%"

        if inst.off() != None:
          if inst.off() > 0:
            label += " + " + str(inst.off())
          elif inst.off() < 0:
            label += " " + str(inst.off())

        expr = "(r{reg:02d} + unsigned(to_signed({imm:s}, 32)))".format(reg = inst.rA(), imm = label)
      else:
        expr = "(r{regA:02d} + r{regB:02d})".format(regA = inst.rA(), regB = inst.rB())

      tw.writeCommentLine(str(inst))
      tw.writeLine("m_addr <= std_logic_vector(" + expr + ");")

      # If the instruction is a read, we need to set the read strobe high.
      # If the instruction is a write, we need to set the write strobe high AND set the data out line.
      if inst.width() == 1:
        tw.writeLine("offset <= ((to_integer((" + expr + ")) mod 4) * 8);")
      elif inst.width() == 2:
        tw.writeLine("offset <= ((to_integer((" + expr + ")) mod 2) * 16);")

      if inst.isRead():
        tw.writeLine("m_rd <= '1';")
        tw.writeLine("m_wr <= \"0000\";")
      else:
        if inst.width() == 1:
          tw.writeLine("m_data_out <= std_logic_vector(unsigned(r{:02d}(7 downto 0))) & std_logic_vector(unsigned(r{:02d}(7 downto 0))) & std_logic_vector(unsigned(r{:02d}(7 downto 0))) & std_logic_vector(unsigned(r{:02d}(7 downto 0)));".format(inst.rD(), inst.rD(), inst.rD(), inst.rD()))
        elif inst.width() == 2:
          tw.writeLine("m_data_out <= std_logic_vector(unsigned(r{:02d}(15 downto 0))) & std_logic_vector(unsigned(r{:02d}(15 downto 0)));".format(inst.rD(), inst.rD()))
        elif inst.width() == 4:
          tw.writeLine("m_data_out <= std_logic_vector(unsigned(r{:02d}));".format(inst.rD()))

        tw.writeLine("m_rd <= '0';")

        # Set up the write enable.
        if inst.width() == 1:
          tw.writeLine("m_wr <= \"0000\";")
          tw.writeLine("m_wr(to_integer(" + expr + ") mod 4) <= '1';")
        elif inst.width() == 2:
          tw.writeLine("m_wr <= \"0000\";")
          tw.writeLine("m_wr((to_integer(" + expr + ") mod 2)*2) <= '1';")
          tw.writeLine("m_wr(((to_integer(" + expr + ") mod 2)*2)+1) <= '1';")
        elif inst.width() == 4:
          tw.writeLine("m_wr <= \"1111\";")
        else:
          raise ValueError("Invalid width " + str(inst.width()) + " while translating instruction " + str(inst) + ".")

      tw.writeBlankLine()

    # Otherwise, this is a computation state, and we need to emit translations of
    # each instruction.
    else:
      tw.writeCommentLine("Computation state. Compute values and store in internal registers.")
      tw.writeBlankLine()
      tw.writeCommentLine("Deassert read and write strobes.")
      tw.writeLine("m_rd <= '0';")
      tw.writeLine("m_wr <= \"0000\";")

      tw.writeBlankLine()

      # First get all inputs to the block.
      tw.writeCommentLine("Inputs: " + ", ".join(list(map(lambda r: "r{:02d}".format(r),
                                                           s.inputs()))))
      for i in s.inputs():
        if i != 0:
          tw.writeLine(localName(s.name(), i) + " := " + "r{:02d}".format(i) + ";")

      tw.writeBlankLine()
      tw.writeCommentLine("Set local carry variable.")
      tw.writeLine("temp_carry := carry;")

      tw.writeBlankLine()

      # Now write translations of all the instructions in the block.
      for inst in s.instructions():
        tw.writeCommentLine(str(inst))
        for line in translateInstruction(s.name(), inst):
          tw.writeLine(line)
        tw.writeBlankLine()

      tw.writeBlankLine()

      # Finally, write locals to their respective permanent registers.
      tw.writeCommentLine("Outputs: " + ", ".join(list(map(lambda r: "r{:02d}".format(r),
                                                           s.outputs()))))
      for o in s.outputs():
        if o != 0:
          tw.writeLine("r{:02d}".format(o) + " <= " + localName(s.name(), o) + ";")

      tw.writeBlankLine()
      tw.writeCommentLine("Set carry flag register.")
      tw.writeLine("carry <= temp_carry;")

      tw.writeBlankLine()

    tw.decreaseIndent()

  tw.writeLine("when others => null;")
  tw.writeLine("end case;")

  tw.decreaseIndent()

  tw.writeLine("end if;")
  tw.decreaseIndent()

  tw.writeLine("else")

  tw.increaseIndent()

  tw.writeCommentLine("Put outputs into tri-state to avoid conflicts.")

  tw.writeLine("m_data_out <= (others => 'Z');")
  tw.writeLine("m_addr     <= (others => 'Z');")

  tw.writeBlankLine()

  for out in stateMachine.outputRegisters():
    tw.writeLine("out_r{:02d} <= (others => 'Z');".format(out))

  tw.writeBlankLine()

  tw.writeLine("m_rd       <= 'Z';")
  tw.writeLine("m_wr       <= \"ZZZZ\";")

  tw.writeLine("carry_out  <= 'Z';")

  tw.decreaseIndent()

  tw.writeLine("end if;")

  tw.decreaseIndent()
  tw.writeLine("end process;")

  tw.decreaseIndent()
  tw.writeLine("end " + entityName + "_behav;")

  return str(tw)