URL
https://opencores.org/ocsvn/opencpu32/opencpu32/trunk
Subversion Repositories opencpu32
[/] [opencpu32/] [trunk/] [hdl/] [opencpu32/] [ControlUnit.vhd] - Rev 36
Go to most recent revision | Compare with Previous | Blame | View Log
--! @file --! @brief ControlUnit http://en.wikipedia.org/wiki/Control_unit --! Use standard library and import the packages (std_logic_1164,std_logic_unsigned,std_logic_arith) library IEEE; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; --! Use CPU Definitions package use work.pkgOpenCPU32.all; --! The control unit coordinates the input and output devices of a computer system. It fetches the code of all of the instructions \n --! in the microprograms. It directs the operation of the other units by providing timing and control signals. \n --! all computer resources are managed by the Control Unit.It directs the flow of data between the cpu and the other devices.\n --! The outputs of the control unit control the activity of the rest of the device. A control unit can be thought of as a finite-state machine. --! The purpose of datapaths is to provide routes for data to travel between functional units. entity ControlUnit is generic (n : integer := nBits - 1); --! Generic value (Used to easily change the size of the Alu on the package) Port ( reset : in STD_LOGIC; clk : in STD_LOGIC; --! Main system clock FlagsDp : in STD_LOGIC_VECTOR (2 downto 0); --! Flags comming from the Datapath DataDp : in STD_LOGIC_VECTOR (n downto 0); --! Data comming from the Datapath outEnDp : out typeEnDis; --! Enable/Disable datapath output MuxDp : out STD_LOGIC_VECTOR (2 downto 0); --! Select on datapath data from (Memory, Imediate, RegFileA, RegFileB, AluOut) MuxRegDp : out STD_LOGIC_VECTOR(1 downto 0); --! Select Alu InputA (Memory,Imediate,RegFileA) ImmDp : out STD_LOGIC_VECTOR (n downto 0); --! Imediate value passed to the Datapath DpAluOp : out aluOps; --! Alu operations DpRegFileWriteAddr : out generalRegisters; --! General register address to write DpRegFileWriteEn : out STD_LOGIC; --! Enable register write DpRegFileReadAddrA : out generalRegisters; --! General register address to read DpRegFileReadAddrB : out generalRegisters; --! General register address to read DpRegFileReadEnA : out STD_LOGIC; --! Enable register read (PortA) DpRegFileReadEnB : out STD_LOGIC; --! Enable register read (PortB) MemoryDataReadEn : out std_logic; --! Enable Main memory read MemoryDataWriteEn: out std_logic; --! Enable Main memory write MemoryDataInput : in STD_LOGIC_VECTOR (n downto 0); --! Incoming data from main memory MemoryDataRdAddr : out STD_LOGIC_VECTOR (n downto 0); --! Main memory Read address MemoryDataWrAddr : out STD_LOGIC_VECTOR (n downto 0); --! Main memory Write address MemoryDataOut : out STD_LOGIC_VECTOR (n downto 0)); --! Data to write on main memory end ControlUnit; --! @brief ControlUnit http://en.wikipedia.org/wiki/Control_unit --! @details The control unit receives external instructions or commands which it converts into a sequence of control signals that the control \n --! unit applies to data path to implement a sequence of register-transfer level operations. architecture Behavioral of ControlUnit is signal currentCpuState : controlUnitStates; -- CPU states signal nextCpuState : controlUnitStates; -- CPU states signal currentExState : executionStates; -- Execution states signal nextExState : executionStates; -- Execution states signal PC : std_logic_vector(n downto 0); -- Program Counter signal IR : std_logic_vector(n downto 0); -- Intruction register signal currInstruction : std_logic_vector(n downto 0); -- Current Intruction begin -- Next state logic (CPU, fetch, decode, execute states) process (clk, reset) begin if (reset = '1') then currentCpuState <= initial; elsif rising_edge(clk) then currentCpuState <= nextCpuState; end if; end process; -- Next state logic (Execution states) process (clk, currentCpuState) begin if (reset = '1') then currentExState <= initInstructionExecution; elsif rising_edge(clk) then currentExState <= nextExState; end if; end process; -- States Fetch, decode, execute from the processor (Also handles the execution of jump instructions) process (currentCpuState) variable cyclesExecute : integer range 0 to 20; -- Cycles to wait while executing instruction variable opcodeIR : std_logic_vector(5 downto 0); variable operand_reg1 : std_logic_vector(3 downto 0); variable operand_imm : std_logic_vector(21 downto 0); begin opcodeIR := IR((IR'HIGH) downto (IR'HIGH - 5)); operand_reg1 := IR((IR'HIGH - 6) downto (IR'HIGH - 9)); -- 4 bits register operand1 (Max 16 registers) operand_imm := IR((IR'HIGH - 10) downto (IR'LOW)); -- 22 bits imediate value (Max value 4194304) case currentCpuState is -- Initial state left from reset ... when initial => cyclesExecute := 0; PC <= (others => '0'); IR <= (others => '0'); MemoryDataRdAddr <= (others => '0'); MemoryDataReadEn <= '0'; MemoryDataWriteEn <= '0'; nextCpuState <= fetch; -- Fetch state (Go to memory and get a instruction) when fetch => -- Increment program counter (Remember that PC will be update only on the next cycle... PC <= PC + conv_std_logic_vector(1, nBits); MemoryDataRdAddr <= PC; -- Warning PC is not 1 yet... IR <= MemoryDataInput; MemoryDataReadEn <= '1'; nextCpuState <= decode; -- Detect with instruction came from memory, set the number of cycles to execute... when decode => MemoryDataReadEn <= '0'; MemoryDataWriteEn <= '0'; -- The high attribute points to the highes bit position case opcodeIR is when mov_reg | mov_val | add_reg | add_val | sub_reg | and_reg | or_reg | xor_reg | ld_reg | ld_val | stom_reg | stom_val => nextCpuState <= execute; cyclesExecute := 1; -- Wait 1 cycles currInstruction <= IR; when jmp_val | jmpr_val => nextCpuState <= execute; cyclesExecute := 0; -- No Wait cycle -- Invalid instruction (Now will be ignored, but latter should raise a trap when others => null; end case; -- Wait while the process that handles the execution works.. when execute => -- On the case of jump instructions, it's execution will be handled on this process case opcodeIR is when jmp_val => PC <= "0000000000" & operand_imm; when jmpr_val => PC <= PC + ("0000000000" & operand_imm); when ld_val => MemoryDataRdAddr <= "0000000000" & operand_imm; MemoryDataReadEn <= '1'; -- STORE r1,10 (Store the value on r1 in the main memory located at address 10) when stom_val => MemoryDataWrAddr <= "0000000000" & operand_imm; MemoryDataWriteEn <= '1'; MemoryDataOut <= DataDp; when others => null; end case; if cyclesExecute = 0 then -- Finish the instruction execution get next nextCpuState <= fetch; else nextCpuState <= executing; end if; -- Just wait a cycle and back again to execute state which verify if still need to wait some cycles when executing => cyclesExecute := cyclesExecute - 1; nextCpuState <= execute; when others => null; end case; end process; -- Process that handles the execution of each instruction (Excluding the call,jump,load,store instructions) process (currentExState) --variable operando1_reg : std_logic_vector(generalRegisters'range); variable opcodeIR : std_logic_vector(5 downto 0); variable operand_reg1 : std_logic_vector(3 downto 0); variable operand_reg2 : std_logic_vector(3 downto 0); variable operand_imm : std_logic_vector(21 downto 0); begin -- Parse the common operands opcodeIR := IR((IR'HIGH) downto (IR'HIGH - 5)); -- 6 Bits opcode (Max 64 instructions) operand_reg1 := IR((IR'HIGH - 6) downto (IR'HIGH - 9)); -- 4 bits register operand1 (Max 16 registers) operand_reg2 := IR((IR'HIGH - 10) downto (IR'HIGH - 13)); -- 4 bits register operand2 (Max 16 registers operand_imm := IR((IR'HIGH - 10) downto (IR'LOW)); -- 22 bits imediate value (Max value 4194304) -- Select the instruction and init it's execution case currentExState is when initInstructionExecution => nextExState <= waitToExecute; when waitToExecute => if ( (currentCpuState /= execute) and (currentCpuState /= executing) ) then nextExState <= initInstructionExecution; else case opcodeIR is -- MOV r2,r1 (See the testDatapath to see how to drive the datapath for this function) when mov_reg => MuxDp <= muxPos(fromRegFileB); DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg2))); DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); DpRegFileReadEnB <= '1'; nextExState <= writeRegister; -- LOAD r1,10 (Load into r1, the value in the main memory located at address 10) when ld_val => MuxDp <= muxPos(fromMemory); DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- The part that interface with the memory is located on the first process nextExState <= writeRegister; -- STORE r1,10 (Store the value on r1 in the main memory located at address 10) when stom_val => MuxDp <= muxPos(fromRegFileB); DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); DpRegFileReadEnB <= '1'; nextExState <= readRegisterB; -- The part that interface with the memory is located on the first process nextExState <= readRegisterB; -- ADD r2,r0 (See the testDatapath to see how to drive the datapath for this function) when add_reg | sub_reg | and_reg | or_reg | xor_reg => MuxDp <= muxPos(fromAlu); MuxRegDp <= muxRegPos(fromRegFileA); DpRegFileReadAddrA <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Read first operand DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg2))); -- Read second operand DpRegFileReadEnA <= '1'; DpRegFileReadEnB <= '1'; DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Point to write in first operand (pointing to register) DpAluOp <= opcode2AluOp(opcodeIR); -- Select the alu operation from the operand nextExState <= writeRegister; -- MOV r0,10d (See the testDatapath to see how to drive the datapath for this function) when mov_val => MuxDp <= muxPos(fromImediate); DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); ImmDp <= "0000000000" & operand_imm; -- & is used to concatenate signals nextExState <= writeRegister; -- ADD r3,2 (r2 <= r2+2) (See the testDatapath to see how to drive the datapath for this function) when add_val | sub_val | and_val | or_val | xor_val => MuxDp <= muxPos(fromAlu); MuxRegDp <= muxRegPos(fromImediate); DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Read first operand DpRegFileReadEnB <= '1'; ImmDp <= "0000000000" & operand_imm; -- & is used to concatenate signals DpAluOp <= opcode2AluOp(opcodeIR); -- Select the alu operation from the operand nextExState <= writeRegister; when others => null; end case; end if; -- Write something on the register files when writeRegister => DpRegFileWriteEn <= '1'; nextExState <= releaseWriteRead; when readRegisterB => DpRegFileReadEnB <= '1'; outEnDp <= enable; nextExState <= releaseWriteRead; when readRegisterA => DpRegFileReadEnA <= '1'; outEnDp <= enable; nextExState <= releaseWriteRead; -- Release lines (Reset Datapath lines to something that does nothing...) when releaseWriteRead => DpRegFileReadEnB <= '0'; DpRegFileReadEnA <= '0'; DpRegFileWriteEn <= '0'; outEnDp <= disable; -- Come back to waiting state nextExState <= waitToExecute; when others => null; end case; end process; end Behavioral;
Go to most recent revision | Compare with Previous | Blame | View Log