| 1 |
22 |
leonardoar |
--! @file
|
| 2 |
|
|
--! @brief ControlUnit http://en.wikipedia.org/wiki/Control_unit
|
| 3 |
|
|
|
| 4 |
|
|
--! Use standard library and import the packages (std_logic_1164,std_logic_unsigned,std_logic_arith)
|
| 5 |
|
|
library IEEE;
|
| 6 |
|
|
use ieee.std_logic_1164.all;
|
| 7 |
|
|
use ieee.std_logic_unsigned.all;
|
| 8 |
|
|
use ieee.std_logic_arith.all;
|
| 9 |
|
|
|
| 10 |
|
|
--! Use CPU Definitions package
|
| 11 |
|
|
use work.pkgOpenCPU32.all;
|
| 12 |
|
|
|
| 13 |
|
|
--! The control unit coordinates the input and output devices of a computer system. It fetches the code of all of the instructions \n
|
| 14 |
|
|
--! in the microprograms. It directs the operation of the other units by providing timing and control signals. \n
|
| 15 |
|
|
--! all computer resources are managed by the Control Unit.It directs the flow of data between the cpu and the other devices.\n
|
| 16 |
|
|
--! 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.
|
| 17 |
|
|
|
| 18 |
|
|
--! The purpose of datapaths is to provide routes for data to travel between functional units.
|
| 19 |
|
|
entity ControlUnit is
|
| 20 |
24 |
leonardoar |
generic (n : integer := nBits - 1); --! Generic value (Used to easily change the size of the Alu on the package)
|
| 21 |
|
|
Port ( reset : in STD_LOGIC;
|
| 22 |
28 |
leonardoar |
clk : in STD_LOGIC; --! Main system clock
|
| 23 |
30 |
leonardoar |
FlagsDp : in STD_LOGIC_VECTOR (2 downto 0); --! Flags comming from the Datapath
|
| 24 |
28 |
leonardoar |
DataDp : in STD_LOGIC_VECTOR (n downto 0); --! Data comming from the Datapath
|
| 25 |
|
|
MuxDp : out STD_LOGIC_VECTOR (2 downto 0); --! Select on datapath data from (Memory, Imediate, RegFileA, RegFileB, AluOut)
|
| 26 |
27 |
leonardoar |
MuxRegDp : out STD_LOGIC_VECTOR(1 downto 0); --! Select Alu InputA (Memory,Imediate,RegFileA)
|
| 27 |
28 |
leonardoar |
ImmDp : out STD_LOGIC_VECTOR (n downto 0); --! Imediate value passed to the Datapath
|
| 28 |
27 |
leonardoar |
DpAluOp : out aluOps; --! Alu operations
|
| 29 |
28 |
leonardoar |
DpRegFileWriteAddr : out generalRegisters; --! General register address to write
|
| 30 |
|
|
DpRegFileWriteEn : out STD_LOGIC; --! Enable register write
|
| 31 |
|
|
DpRegFileReadAddrA : out generalRegisters; --! General register address to read
|
| 32 |
|
|
DpRegFileReadAddrB : out generalRegisters; --! General register address to read
|
| 33 |
|
|
DpRegFileReadEnA : out STD_LOGIC; --! Enable register read (PortA)
|
| 34 |
|
|
DpRegFileReadEnB : out STD_LOGIC; --! Enable register read (PortB)
|
| 35 |
|
|
MemoryDataReadEn : out std_logic; --! Enable Main memory read
|
| 36 |
|
|
MemoryDataWriteEn: out std_logic; --! Enable Main memory write
|
| 37 |
|
|
MemoryDataInput : in STD_LOGIC_VECTOR (n downto 0); --! Incoming data from main memory
|
| 38 |
30 |
leonardoar |
MemoryDataRdAddr : out STD_LOGIC_VECTOR (n downto 0); --! Main memory Read address
|
| 39 |
|
|
MemoryDataWrAddr : out STD_LOGIC_VECTOR (n downto 0); --! Main memory Write address
|
| 40 |
28 |
leonardoar |
MemoryDataOut : out STD_LOGIC_VECTOR (n downto 0)); --! Data to write on main memory
|
| 41 |
22 |
leonardoar |
end ControlUnit;
|
| 42 |
|
|
|
| 43 |
|
|
--! @brief ControlUnit http://en.wikipedia.org/wiki/Control_unit
|
| 44 |
|
|
--! @details The control unit receives external instructions or commands which it converts into a sequence of control signals that the control \n
|
| 45 |
|
|
--! unit applies to data path to implement a sequence of register-transfer level operations.
|
| 46 |
|
|
architecture Behavioral of ControlUnit is
|
| 47 |
26 |
leonardoar |
|
| 48 |
25 |
leonardoar |
signal currentCpuState : controlUnitStates; -- CPU states
|
| 49 |
|
|
signal nextCpuState : controlUnitStates; -- CPU states
|
| 50 |
26 |
leonardoar |
|
| 51 |
|
|
signal currentExState : executionStates; -- Execution states
|
| 52 |
|
|
signal nextExState : executionStates; -- Execution states
|
| 53 |
|
|
|
| 54 |
25 |
leonardoar |
signal PC : std_logic_vector(n downto 0); -- Program Counter
|
| 55 |
|
|
signal IR : std_logic_vector(n downto 0); -- Intruction register
|
| 56 |
|
|
signal currInstruction : std_logic_vector(n downto 0); -- Current Intruction
|
| 57 |
22 |
leonardoar |
begin
|
| 58 |
24 |
leonardoar |
|
| 59 |
26 |
leonardoar |
-- Next state logic (CPU, fetch, decode, execute states)
|
| 60 |
24 |
leonardoar |
process (clk, reset)
|
| 61 |
|
|
begin
|
| 62 |
|
|
if (reset = '1') then
|
| 63 |
26 |
leonardoar |
currentCpuState <= initial;
|
| 64 |
24 |
leonardoar |
elsif rising_edge(clk) then
|
| 65 |
|
|
currentCpuState <= nextCpuState;
|
| 66 |
|
|
end if;
|
| 67 |
|
|
end process;
|
| 68 |
|
|
|
| 69 |
26 |
leonardoar |
-- Next state logic (Execution states)
|
| 70 |
|
|
process (clk, currentCpuState)
|
| 71 |
|
|
begin
|
| 72 |
27 |
leonardoar |
if ( (currentCpuState /= execute) and (currentCpuState /= executing) ) then
|
| 73 |
|
|
currentExState <= initInstructionExecution;
|
| 74 |
26 |
leonardoar |
elsif rising_edge(clk) then
|
| 75 |
|
|
currentExState <= nextExState;
|
| 76 |
|
|
end if;
|
| 77 |
|
|
end process;
|
| 78 |
|
|
|
| 79 |
29 |
leonardoar |
-- States Fetch, decode, execute from the processor (Also handles the execution of jump instructions)
|
| 80 |
24 |
leonardoar |
process (currentCpuState)
|
| 81 |
25 |
leonardoar |
variable cyclesExecute : integer range 0 to 20; -- Cycles to wait while executing instruction
|
| 82 |
29 |
leonardoar |
variable opcodeIR : std_logic_vector(5 downto 0);
|
| 83 |
|
|
variable operand_reg1 : std_logic_vector(3 downto 0);
|
| 84 |
|
|
variable operand_imm : std_logic_vector(21 downto 0);
|
| 85 |
24 |
leonardoar |
begin
|
| 86 |
29 |
leonardoar |
opcodeIR := IR((IR'HIGH) downto (IR'HIGH - 5));
|
| 87 |
|
|
operand_reg1 := IR((IR'HIGH - 6) downto (IR'HIGH - 9)); -- 4 bits register operand1 (Max 16 registers)
|
| 88 |
|
|
operand_imm := IR((IR'HIGH - 10) downto (IR'LOW)); -- 22 bits imediate value (Max value 4194304)
|
| 89 |
24 |
leonardoar |
case currentCpuState is
|
| 90 |
|
|
-- Initial state left from reset ...
|
| 91 |
|
|
when initial =>
|
| 92 |
25 |
leonardoar |
cyclesExecute := 0;
|
| 93 |
24 |
leonardoar |
PC <= (others => '0');
|
| 94 |
25 |
leonardoar |
IR <= (others => '0');
|
| 95 |
30 |
leonardoar |
MemoryDataRdAddr <= (others => '0');
|
| 96 |
28 |
leonardoar |
MemoryDataReadEn <= '0';
|
| 97 |
30 |
leonardoar |
MemoryDataWriteEn <= '0';
|
| 98 |
24 |
leonardoar |
nextCpuState <= fetch;
|
| 99 |
|
|
|
| 100 |
|
|
-- Fetch state (Go to memory and get a instruction)
|
| 101 |
|
|
when fetch =>
|
| 102 |
|
|
-- Increment program counter (Remember that PC will be update only on the next cycle...
|
| 103 |
|
|
PC <= PC + conv_std_logic_vector(1, nBits);
|
| 104 |
30 |
leonardoar |
MemoryDataRdAddr <= PC; -- Warning PC is not 1 yet...
|
| 105 |
24 |
leonardoar |
IR <= MemoryDataInput;
|
| 106 |
28 |
leonardoar |
MemoryDataReadEn <= '1';
|
| 107 |
24 |
leonardoar |
nextCpuState <= decode;
|
| 108 |
|
|
|
| 109 |
25 |
leonardoar |
-- Detect with instruction came from memory, set the number of cycles to execute...
|
| 110 |
24 |
leonardoar |
when decode =>
|
| 111 |
28 |
leonardoar |
MemoryDataReadEn <= '0';
|
| 112 |
|
|
MemoryDataWriteEn <= '0';
|
| 113 |
25 |
leonardoar |
|
| 114 |
|
|
-- The high attribute points to the highes bit position
|
| 115 |
26 |
leonardoar |
case opcodeIR is
|
| 116 |
27 |
leonardoar |
when mov_reg | mov_val | add_reg | sub_reg | and_reg | or_reg | xor_reg =>
|
| 117 |
25 |
leonardoar |
nextCpuState <= execute;
|
| 118 |
27 |
leonardoar |
cyclesExecute := 3; -- Wait 3 cycles for mov operation
|
| 119 |
25 |
leonardoar |
currInstruction <= IR;
|
| 120 |
29 |
leonardoar |
|
| 121 |
|
|
when jmp_val | jmpr_val =>
|
| 122 |
|
|
nextCpuState <= execute;
|
| 123 |
|
|
cyclesExecute := 1;
|
| 124 |
|
|
|
| 125 |
26 |
leonardoar |
-- Invalid instruction (Now will be ignored, but latter should raise a trap
|
| 126 |
29 |
leonardoar |
when others =>
|
| 127 |
|
|
null;
|
| 128 |
25 |
leonardoar |
end case;
|
| 129 |
24 |
leonardoar |
|
| 130 |
25 |
leonardoar |
-- Wait while the process that handles the execution works..
|
| 131 |
|
|
when execute =>
|
| 132 |
29 |
leonardoar |
-- On the case of jump instructions, it's execution will be handled on this process
|
| 133 |
|
|
case opcodeIR is
|
| 134 |
|
|
when jmp_val =>
|
| 135 |
|
|
PC <= "0000000000" & operand_imm;
|
| 136 |
|
|
when jmpr_val =>
|
| 137 |
|
|
PC <= PC + ("0000000000" & operand_imm);
|
| 138 |
|
|
when others =>
|
| 139 |
|
|
null;
|
| 140 |
|
|
end case;
|
| 141 |
|
|
|
| 142 |
26 |
leonardoar |
if cyclesExecute = 0 then
|
| 143 |
|
|
-- Finish the instruction execution get next
|
| 144 |
25 |
leonardoar |
nextCpuState <= fetch;
|
| 145 |
26 |
leonardoar |
else
|
| 146 |
|
|
nextCpuState <= executing;
|
| 147 |
|
|
end if;
|
| 148 |
|
|
|
| 149 |
|
|
-- Just wait a cycle and back again to execute state which verify if still need to wait some cycles
|
| 150 |
|
|
when executing =>
|
| 151 |
|
|
cyclesExecute := cyclesExecute - 1;
|
| 152 |
|
|
nextCpuState <= execute;
|
| 153 |
|
|
|
| 154 |
24 |
leonardoar |
when others =>
|
| 155 |
|
|
null;
|
| 156 |
|
|
end case;
|
| 157 |
25 |
leonardoar |
end process;
|
| 158 |
|
|
|
| 159 |
29 |
leonardoar |
-- Process that handles the execution of each instruction (Excluding the call and jump instructions)
|
| 160 |
26 |
leonardoar |
process (currentExState)
|
| 161 |
|
|
--variable operando1_reg : std_logic_vector(generalRegisters'range);
|
| 162 |
27 |
leonardoar |
variable opcodeIR : std_logic_vector(5 downto 0);
|
| 163 |
|
|
variable operand_reg1 : std_logic_vector(3 downto 0);
|
| 164 |
|
|
variable operand_reg2 : std_logic_vector(3 downto 0);
|
| 165 |
|
|
variable operand_imm : std_logic_vector(21 downto 0);
|
| 166 |
25 |
leonardoar |
begin
|
| 167 |
27 |
leonardoar |
-- Parse the common operands
|
| 168 |
|
|
opcodeIR := IR((IR'HIGH) downto (IR'HIGH - 5)); -- 6 Bits opcode (Max 64 instructions)
|
| 169 |
|
|
operand_reg1 := IR((IR'HIGH - 6) downto (IR'HIGH - 9)); -- 4 bits register operand1 (Max 16 registers)
|
| 170 |
|
|
operand_reg2 := IR((IR'HIGH - 10) downto (IR'HIGH - 13)); -- 4 bits register operand2 (Max 16 registers
|
| 171 |
|
|
operand_imm := IR((IR'HIGH - 10) downto (IR'LOW)); -- 22 bits imediate value (Max value 4194304)
|
| 172 |
|
|
|
| 173 |
|
|
-- Select the instruction and init it's execution
|
| 174 |
26 |
leonardoar |
case currentExState is
|
| 175 |
27 |
leonardoar |
when initInstructionExecution =>
|
| 176 |
|
|
case opcodeIR is
|
| 177 |
|
|
-- MOV r2,r1 (See the testDatapath to see how to drive the datapath for this function)
|
| 178 |
|
|
when mov_reg =>
|
| 179 |
|
|
MuxDp <= muxPos(fromRegFileB);
|
| 180 |
|
|
DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg2)));
|
| 181 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
| 182 |
|
|
DpRegFileReadEnB <= '1';
|
| 183 |
|
|
nextExState <= writeRegister;
|
| 184 |
|
|
|
| 185 |
|
|
-- ADD r2,r0 (See the testDatapath to see how to drive the datapath for this function)
|
| 186 |
|
|
when add_reg | sub_reg | and_reg | or_reg | xor_reg =>
|
| 187 |
|
|
MuxDp <= muxPos(fromAlu);
|
| 188 |
|
|
MuxRegDp <= muxRegPos(fromRegFileA);
|
| 189 |
|
|
DpRegFileReadAddrA <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Read first operand
|
| 190 |
|
|
DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg2))); -- Read second operand
|
| 191 |
|
|
DpRegFileReadEnA <= '1';
|
| 192 |
|
|
DpRegFileReadEnB <= '1';
|
| 193 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Point to write in first operand (pointing to register)
|
| 194 |
|
|
DpAluOp <= opcode2AluOp(opcodeIR); -- Select the alu operation from the operand
|
| 195 |
|
|
nextExState <= writeRegister;
|
| 196 |
|
|
|
| 197 |
|
|
-- MOV r0,10d (See the testDatapath to see how to drive the datapath for this function)
|
| 198 |
|
|
when mov_val =>
|
| 199 |
|
|
MuxDp <= muxPos(fromImediate);
|
| 200 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
| 201 |
|
|
ImmDp <= "0000000000" & operand_imm; -- & is used to concatenate signals
|
| 202 |
|
|
nextExState <= writeRegister;
|
| 203 |
|
|
|
| 204 |
|
|
-- ADD r3,2 (r2 <= r2+2) (See the testDatapath to see how to drive the datapath for this function)
|
| 205 |
|
|
when add_val | sub_val | and_val | or_val | xor_val =>
|
| 206 |
|
|
MuxDp <= muxPos(fromAlu);
|
| 207 |
|
|
MuxRegDp <= muxRegPos(fromImediate);
|
| 208 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
| 209 |
|
|
DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Read first operand
|
| 210 |
|
|
DpRegFileReadEnB <= '1';
|
| 211 |
|
|
ImmDp <= "0000000000" & operand_imm; -- & is used to concatenate signals
|
| 212 |
|
|
DpAluOp <= opcode2AluOp(opcodeIR); -- Select the alu operation from the operand
|
| 213 |
|
|
nextExState <= writeRegister;
|
| 214 |
|
|
|
| 215 |
26 |
leonardoar |
when others =>
|
| 216 |
|
|
null;
|
| 217 |
|
|
end case;
|
| 218 |
27 |
leonardoar |
|
| 219 |
|
|
-- Write something on the register files
|
| 220 |
|
|
when writeRegister =>
|
| 221 |
|
|
DpRegFileWriteEn <= '1';
|
| 222 |
|
|
nextExState <= releaseWriteRead;
|
| 223 |
|
|
|
| 224 |
|
|
-- Release lines (Reset Datapath lines to something that does nothing...)
|
| 225 |
|
|
when releaseWriteRead =>
|
| 226 |
|
|
DpRegFileReadEnB <= '0';
|
| 227 |
|
|
DpRegFileReadEnA <= '0';
|
| 228 |
|
|
DpRegFileWriteEn <= '0';
|
| 229 |
|
|
|
| 230 |
26 |
leonardoar |
when others =>
|
| 231 |
|
|
null;
|
| 232 |
|
|
end case;
|
| 233 |
24 |
leonardoar |
end process;
|
| 234 |
22 |
leonardoar |
|
| 235 |
|
|
end Behavioral;
|
| 236 |
|
|
|