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 |
31 |
leonardoar |
outEnDp : out typeEnDis; --! Enable/Disable datapath output
|
26 |
28 |
leonardoar |
MuxDp : out STD_LOGIC_VECTOR (2 downto 0); --! Select on datapath data from (Memory, Imediate, RegFileA, RegFileB, AluOut)
|
27 |
27 |
leonardoar |
MuxRegDp : out STD_LOGIC_VECTOR(1 downto 0); --! Select Alu InputA (Memory,Imediate,RegFileA)
|
28 |
28 |
leonardoar |
ImmDp : out STD_LOGIC_VECTOR (n downto 0); --! Imediate value passed to the Datapath
|
29 |
27 |
leonardoar |
DpAluOp : out aluOps; --! Alu operations
|
30 |
28 |
leonardoar |
DpRegFileWriteAddr : out generalRegisters; --! General register address to write
|
31 |
|
|
DpRegFileWriteEn : out STD_LOGIC; --! Enable register write
|
32 |
|
|
DpRegFileReadAddrA : out generalRegisters; --! General register address to read
|
33 |
|
|
DpRegFileReadAddrB : out generalRegisters; --! General register address to read
|
34 |
|
|
DpRegFileReadEnA : out STD_LOGIC; --! Enable register read (PortA)
|
35 |
|
|
DpRegFileReadEnB : out STD_LOGIC; --! Enable register read (PortB)
|
36 |
|
|
MemoryDataReadEn : out std_logic; --! Enable Main memory read
|
37 |
|
|
MemoryDataWriteEn: out std_logic; --! Enable Main memory write
|
38 |
|
|
MemoryDataInput : in STD_LOGIC_VECTOR (n downto 0); --! Incoming data from main memory
|
39 |
30 |
leonardoar |
MemoryDataRdAddr : out STD_LOGIC_VECTOR (n downto 0); --! Main memory Read address
|
40 |
|
|
MemoryDataWrAddr : out STD_LOGIC_VECTOR (n downto 0); --! Main memory Write address
|
41 |
28 |
leonardoar |
MemoryDataOut : out STD_LOGIC_VECTOR (n downto 0)); --! Data to write on main memory
|
42 |
22 |
leonardoar |
end ControlUnit;
|
43 |
|
|
|
44 |
|
|
--! @brief ControlUnit http://en.wikipedia.org/wiki/Control_unit
|
45 |
|
|
--! @details The control unit receives external instructions or commands which it converts into a sequence of control signals that the control \n
|
46 |
|
|
--! unit applies to data path to implement a sequence of register-transfer level operations.
|
47 |
|
|
architecture Behavioral of ControlUnit is
|
48 |
26 |
leonardoar |
|
49 |
25 |
leonardoar |
signal currentCpuState : controlUnitStates; -- CPU states
|
50 |
|
|
signal nextCpuState : controlUnitStates; -- CPU states
|
51 |
26 |
leonardoar |
|
52 |
|
|
signal currentExState : executionStates; -- Execution states
|
53 |
|
|
signal nextExState : executionStates; -- Execution states
|
54 |
|
|
|
55 |
25 |
leonardoar |
signal PC : std_logic_vector(n downto 0); -- Program Counter
|
56 |
|
|
signal IR : std_logic_vector(n downto 0); -- Intruction register
|
57 |
|
|
signal currInstruction : std_logic_vector(n downto 0); -- Current Intruction
|
58 |
22 |
leonardoar |
begin
|
59 |
24 |
leonardoar |
|
60 |
26 |
leonardoar |
-- Next state logic (CPU, fetch, decode, execute states)
|
61 |
24 |
leonardoar |
process (clk, reset)
|
62 |
|
|
begin
|
63 |
|
|
if (reset = '1') then
|
64 |
26 |
leonardoar |
currentCpuState <= initial;
|
65 |
24 |
leonardoar |
elsif rising_edge(clk) then
|
66 |
|
|
currentCpuState <= nextCpuState;
|
67 |
|
|
end if;
|
68 |
|
|
end process;
|
69 |
|
|
|
70 |
26 |
leonardoar |
-- Next state logic (Execution states)
|
71 |
|
|
process (clk, currentCpuState)
|
72 |
|
|
begin
|
73 |
33 |
leonardoar |
if (reset = '1') then
|
74 |
27 |
leonardoar |
currentExState <= initInstructionExecution;
|
75 |
26 |
leonardoar |
elsif rising_edge(clk) then
|
76 |
|
|
currentExState <= nextExState;
|
77 |
|
|
end if;
|
78 |
|
|
end process;
|
79 |
|
|
|
80 |
29 |
leonardoar |
-- States Fetch, decode, execute from the processor (Also handles the execution of jump instructions)
|
81 |
24 |
leonardoar |
process (currentCpuState)
|
82 |
25 |
leonardoar |
variable cyclesExecute : integer range 0 to 20; -- Cycles to wait while executing instruction
|
83 |
29 |
leonardoar |
variable opcodeIR : std_logic_vector(5 downto 0);
|
84 |
|
|
variable operand_reg1 : std_logic_vector(3 downto 0);
|
85 |
|
|
variable operand_imm : std_logic_vector(21 downto 0);
|
86 |
24 |
leonardoar |
begin
|
87 |
29 |
leonardoar |
opcodeIR := IR((IR'HIGH) downto (IR'HIGH - 5));
|
88 |
|
|
operand_reg1 := IR((IR'HIGH - 6) downto (IR'HIGH - 9)); -- 4 bits register operand1 (Max 16 registers)
|
89 |
|
|
operand_imm := IR((IR'HIGH - 10) downto (IR'LOW)); -- 22 bits imediate value (Max value 4194304)
|
90 |
24 |
leonardoar |
case currentCpuState is
|
91 |
|
|
-- Initial state left from reset ...
|
92 |
|
|
when initial =>
|
93 |
25 |
leonardoar |
cyclesExecute := 0;
|
94 |
24 |
leonardoar |
PC <= (others => '0');
|
95 |
25 |
leonardoar |
IR <= (others => '0');
|
96 |
30 |
leonardoar |
MemoryDataRdAddr <= (others => '0');
|
97 |
28 |
leonardoar |
MemoryDataReadEn <= '0';
|
98 |
30 |
leonardoar |
MemoryDataWriteEn <= '0';
|
99 |
24 |
leonardoar |
nextCpuState <= fetch;
|
100 |
|
|
|
101 |
|
|
-- Fetch state (Go to memory and get a instruction)
|
102 |
|
|
when fetch =>
|
103 |
|
|
-- Increment program counter (Remember that PC will be update only on the next cycle...
|
104 |
|
|
PC <= PC + conv_std_logic_vector(1, nBits);
|
105 |
30 |
leonardoar |
MemoryDataRdAddr <= PC; -- Warning PC is not 1 yet...
|
106 |
24 |
leonardoar |
IR <= MemoryDataInput;
|
107 |
28 |
leonardoar |
MemoryDataReadEn <= '1';
|
108 |
24 |
leonardoar |
nextCpuState <= decode;
|
109 |
|
|
|
110 |
25 |
leonardoar |
-- Detect with instruction came from memory, set the number of cycles to execute...
|
111 |
24 |
leonardoar |
when decode =>
|
112 |
28 |
leonardoar |
MemoryDataReadEn <= '0';
|
113 |
|
|
MemoryDataWriteEn <= '0';
|
114 |
25 |
leonardoar |
|
115 |
|
|
-- The high attribute points to the highes bit position
|
116 |
26 |
leonardoar |
case opcodeIR is
|
117 |
35 |
leonardoar |
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 =>
|
118 |
25 |
leonardoar |
nextCpuState <= execute;
|
119 |
34 |
leonardoar |
cyclesExecute := 1; -- Wait 1 cycles
|
120 |
25 |
leonardoar |
currInstruction <= IR;
|
121 |
29 |
leonardoar |
|
122 |
|
|
when jmp_val | jmpr_val =>
|
123 |
|
|
nextCpuState <= execute;
|
124 |
34 |
leonardoar |
cyclesExecute := 0; -- No Wait cycle
|
125 |
29 |
leonardoar |
|
126 |
26 |
leonardoar |
-- Invalid instruction (Now will be ignored, but latter should raise a trap
|
127 |
29 |
leonardoar |
when others =>
|
128 |
|
|
null;
|
129 |
25 |
leonardoar |
end case;
|
130 |
24 |
leonardoar |
|
131 |
25 |
leonardoar |
-- Wait while the process that handles the execution works..
|
132 |
|
|
when execute =>
|
133 |
29 |
leonardoar |
-- On the case of jump instructions, it's execution will be handled on this process
|
134 |
|
|
case opcodeIR is
|
135 |
31 |
leonardoar |
|
136 |
29 |
leonardoar |
when jmp_val =>
|
137 |
|
|
PC <= "0000000000" & operand_imm;
|
138 |
31 |
leonardoar |
|
139 |
29 |
leonardoar |
when jmpr_val =>
|
140 |
|
|
PC <= PC + ("0000000000" & operand_imm);
|
141 |
31 |
leonardoar |
|
142 |
|
|
when ld_val =>
|
143 |
|
|
MemoryDataRdAddr <= "0000000000" & operand_imm;
|
144 |
|
|
MemoryDataReadEn <= '1';
|
145 |
|
|
|
146 |
|
|
-- STORE r1,10 (Store the value on r1 in the main memory located at address 10)
|
147 |
|
|
when stom_val =>
|
148 |
|
|
MemoryDataWrAddr <= "0000000000" & operand_imm;
|
149 |
|
|
MemoryDataWriteEn <= '1';
|
150 |
|
|
MemoryDataOut <= DataDp;
|
151 |
|
|
|
152 |
29 |
leonardoar |
when others =>
|
153 |
|
|
null;
|
154 |
|
|
end case;
|
155 |
|
|
|
156 |
26 |
leonardoar |
if cyclesExecute = 0 then
|
157 |
|
|
-- Finish the instruction execution get next
|
158 |
25 |
leonardoar |
nextCpuState <= fetch;
|
159 |
26 |
leonardoar |
else
|
160 |
|
|
nextCpuState <= executing;
|
161 |
|
|
end if;
|
162 |
|
|
|
163 |
|
|
-- Just wait a cycle and back again to execute state which verify if still need to wait some cycles
|
164 |
|
|
when executing =>
|
165 |
|
|
cyclesExecute := cyclesExecute - 1;
|
166 |
|
|
nextCpuState <= execute;
|
167 |
|
|
|
168 |
24 |
leonardoar |
when others =>
|
169 |
|
|
null;
|
170 |
|
|
end case;
|
171 |
25 |
leonardoar |
end process;
|
172 |
|
|
|
173 |
31 |
leonardoar |
-- Process that handles the execution of each instruction (Excluding the call,jump,load,store instructions)
|
174 |
26 |
leonardoar |
process (currentExState)
|
175 |
|
|
--variable operando1_reg : std_logic_vector(generalRegisters'range);
|
176 |
27 |
leonardoar |
variable opcodeIR : std_logic_vector(5 downto 0);
|
177 |
|
|
variable operand_reg1 : std_logic_vector(3 downto 0);
|
178 |
|
|
variable operand_reg2 : std_logic_vector(3 downto 0);
|
179 |
|
|
variable operand_imm : std_logic_vector(21 downto 0);
|
180 |
25 |
leonardoar |
begin
|
181 |
27 |
leonardoar |
-- Parse the common operands
|
182 |
|
|
opcodeIR := IR((IR'HIGH) downto (IR'HIGH - 5)); -- 6 Bits opcode (Max 64 instructions)
|
183 |
|
|
operand_reg1 := IR((IR'HIGH - 6) downto (IR'HIGH - 9)); -- 4 bits register operand1 (Max 16 registers)
|
184 |
|
|
operand_reg2 := IR((IR'HIGH - 10) downto (IR'HIGH - 13)); -- 4 bits register operand2 (Max 16 registers
|
185 |
|
|
operand_imm := IR((IR'HIGH - 10) downto (IR'LOW)); -- 22 bits imediate value (Max value 4194304)
|
186 |
|
|
|
187 |
|
|
-- Select the instruction and init it's execution
|
188 |
26 |
leonardoar |
case currentExState is
|
189 |
27 |
leonardoar |
when initInstructionExecution =>
|
190 |
33 |
leonardoar |
nextExState <= waitToExecute;
|
191 |
|
|
|
192 |
|
|
when waitToExecute =>
|
193 |
|
|
if ( (currentCpuState /= execute) and (currentCpuState /= executing) ) then
|
194 |
|
|
nextExState <= initInstructionExecution;
|
195 |
|
|
else
|
196 |
|
|
case opcodeIR is
|
197 |
27 |
leonardoar |
-- MOV r2,r1 (See the testDatapath to see how to drive the datapath for this function)
|
198 |
|
|
when mov_reg =>
|
199 |
|
|
MuxDp <= muxPos(fromRegFileB);
|
200 |
|
|
DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg2)));
|
201 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
202 |
|
|
DpRegFileReadEnB <= '1';
|
203 |
|
|
nextExState <= writeRegister;
|
204 |
|
|
|
205 |
31 |
leonardoar |
-- LOAD r1,10 (Load into r1, the value in the main memory located at address 10)
|
206 |
|
|
when ld_val =>
|
207 |
|
|
MuxDp <= muxPos(fromMemory);
|
208 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
209 |
|
|
-- The part that interface with the memory is located on the first process
|
210 |
|
|
nextExState <= writeRegister;
|
211 |
|
|
|
212 |
|
|
-- STORE r1,10 (Store the value on r1 in the main memory located at address 10)
|
213 |
|
|
when stom_val =>
|
214 |
|
|
MuxDp <= muxPos(fromRegFileB);
|
215 |
|
|
DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
216 |
|
|
DpRegFileReadEnB <= '1';
|
217 |
|
|
nextExState <= readRegisterB;
|
218 |
|
|
-- The part that interface with the memory is located on the first process
|
219 |
|
|
nextExState <= readRegisterB;
|
220 |
|
|
|
221 |
27 |
leonardoar |
-- ADD r2,r0 (See the testDatapath to see how to drive the datapath for this function)
|
222 |
|
|
when add_reg | sub_reg | and_reg | or_reg | xor_reg =>
|
223 |
|
|
MuxDp <= muxPos(fromAlu);
|
224 |
|
|
MuxRegDp <= muxRegPos(fromRegFileA);
|
225 |
|
|
DpRegFileReadAddrA <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Read first operand
|
226 |
|
|
DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg2))); -- Read second operand
|
227 |
|
|
DpRegFileReadEnA <= '1';
|
228 |
|
|
DpRegFileReadEnB <= '1';
|
229 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Point to write in first operand (pointing to register)
|
230 |
|
|
DpAluOp <= opcode2AluOp(opcodeIR); -- Select the alu operation from the operand
|
231 |
|
|
nextExState <= writeRegister;
|
232 |
|
|
|
233 |
|
|
-- MOV r0,10d (See the testDatapath to see how to drive the datapath for this function)
|
234 |
|
|
when mov_val =>
|
235 |
|
|
MuxDp <= muxPos(fromImediate);
|
236 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
237 |
|
|
ImmDp <= "0000000000" & operand_imm; -- & is used to concatenate signals
|
238 |
|
|
nextExState <= writeRegister;
|
239 |
|
|
|
240 |
|
|
-- ADD r3,2 (r2 <= r2+2) (See the testDatapath to see how to drive the datapath for this function)
|
241 |
|
|
when add_val | sub_val | and_val | or_val | xor_val =>
|
242 |
|
|
MuxDp <= muxPos(fromAlu);
|
243 |
|
|
MuxRegDp <= muxRegPos(fromImediate);
|
244 |
|
|
DpRegFileWriteAddr <= Num2reg(conv_integer(UNSIGNED(operand_reg1)));
|
245 |
|
|
DpRegFileReadAddrB <= Num2reg(conv_integer(UNSIGNED(operand_reg1))); -- Read first operand
|
246 |
|
|
DpRegFileReadEnB <= '1';
|
247 |
|
|
ImmDp <= "0000000000" & operand_imm; -- & is used to concatenate signals
|
248 |
|
|
DpAluOp <= opcode2AluOp(opcodeIR); -- Select the alu operation from the operand
|
249 |
|
|
nextExState <= writeRegister;
|
250 |
|
|
|
251 |
26 |
leonardoar |
when others =>
|
252 |
|
|
null;
|
253 |
33 |
leonardoar |
end case;
|
254 |
|
|
end if;
|
255 |
27 |
leonardoar |
|
256 |
|
|
-- Write something on the register files
|
257 |
|
|
when writeRegister =>
|
258 |
|
|
DpRegFileWriteEn <= '1';
|
259 |
|
|
nextExState <= releaseWriteRead;
|
260 |
|
|
|
261 |
31 |
leonardoar |
when readRegisterB =>
|
262 |
|
|
DpRegFileReadEnB <= '1';
|
263 |
|
|
outEnDp <= enable;
|
264 |
|
|
nextExState <= releaseWriteRead;
|
265 |
|
|
|
266 |
|
|
when readRegisterA =>
|
267 |
|
|
DpRegFileReadEnA <= '1';
|
268 |
|
|
outEnDp <= enable;
|
269 |
|
|
nextExState <= releaseWriteRead;
|
270 |
|
|
|
271 |
27 |
leonardoar |
-- Release lines (Reset Datapath lines to something that does nothing...)
|
272 |
|
|
when releaseWriteRead =>
|
273 |
|
|
DpRegFileReadEnB <= '0';
|
274 |
|
|
DpRegFileReadEnA <= '0';
|
275 |
31 |
leonardoar |
DpRegFileWriteEn <= '0';
|
276 |
33 |
leonardoar |
outEnDp <= disable;
|
277 |
|
|
-- Come back to waiting state
|
278 |
34 |
leonardoar |
nextExState <= waitToExecute;
|
279 |
27 |
leonardoar |
|
280 |
26 |
leonardoar |
when others =>
|
281 |
|
|
null;
|
282 |
|
|
end case;
|
283 |
24 |
leonardoar |
end process;
|
284 |
22 |
leonardoar |
|
285 |
|
|
end Behavioral;
|
286 |
|
|
|