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)