DLX-Microprocessor / components / micro_CU.vhd
micro_CU.vhd
Raw
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

use work.constants.all;
use work.myTypes.all;
-- We have taken the assumption that the Decode unit gives only the valuable bits of the function
-- field. In the laboratory case is two. This is useful to use them to directly drive the ALU
-- configuration

-- To make the uprogrammed control unit even more generic we could output only the control word
-- in any needed size and then a wrapper entity is done to connect 
-- the needed output signals of the CW to drive a datapath.
-- As the datapath is defined in the laboratory, then the control unit is made ad hoc
-- for this datapath

entity micro_CU is
  generic (
    OPCODE_SIZE     : integer := 6; -- Number Bits of the OPCODE
    FUNCITON_SIZE   : integer := 2; -- Number Bits of the FUNCTION
    CW_SIZE         : integer := 13; -- Number Bits of the Control Word
    OP_CLOCK_NEEDED : integer := 3 -- Number control words/stages/etc per instruction
  );
  port (
    clk    : in std_logic;
    reset  : in std_logic;
    opcode : in std_logic_vector(OPCODE_SIZE - 1 downto 0);
    func   : in std_logic_vector(FUNCITON_SIZE - 1 downto 0);
    rdy    : in std_logic; -- Used to enable a new OPCODE fetch
    -- Stage 1
    rf_rd1 : out std_logic; -- Enable signal for read 1 of the RF
    rf_rd2 : out std_logic; -- Enable signal for read 2 of the RF
    en_1   : out std_logic; -- Enable signal for the first stage for A,B, in_1,in_2, rd_1
    -- Stage 2
    s_a    : out std_logic; -- Enable signal for selecting operand A
    s_b    : out std_logic; -- Enable signal for selecting operand B
    alu_op : out std_logic_vector (1 downto 0);
    en_2   : out std_logic; -- Enable signal for the second stage ALU output/data memory, rd_2
    -- Stage 3
    s_am : out std_logic; -- Enable signal for selecting alu or memory result
    en_3 : out std_logic; -- Enable signal for the memory
    wrf  : out std_logic; -- Write enable for the RF
    mr   : out std_logic; -- Enable signal for the memory read request
    mw   : out std_logic -- Enable signal for the memory write request
  );
end entity;
architecture rtl of micro_CU is
  constant reloc_size : integer := 2 ** OPCODE_SIZE;
  -- In our case there are only 3 cw per instruction thus is multiplied by 3
  -- size is 2 ** OPCODE_SIZE * OP_CLOCK_NEEDED in this case OP_CLOCK_NEEDED
  constant ucode_mem_size : integer := 2 ** OPCODE_SIZE * OP_CLOCK_NEEDED;
  -- OPCODE to memory address translator
  -- We could also not use a memory translator and add +3 to each OPCODE to find the base address
  -- Note that then there will edge cases (R-Type instruction) in instruction that have the same opcode
  -- in a generic case the formula could be OPCODDE * (OP_CLOCK_NEEDED * FUNCTION)
  type opcode_mem_tr is array (0 to reloc_size - 1) of std_logic_vector(integer(ceil(log2(real(ucode_mem_size)))) - 1 downto 0);
  signal base_addr_tr : opcode_mem_tr := (
  -- used to translate the opcode to its base address in the memory
  -- initialized for OPCODE_SIZE to be on 6 bits
  "00000000", -- 000000 NOP
  "00000011", -- 000001 ADD - SUB - AND - OR
  "00000000", -- 000010 NOP
  "00000000", -- 000011 NOP
  "00000000", -- 000100 NOP
  "00000000", -- 000101 NOP
  "00000000", -- 000110 NOP
  "00000000", -- 000111 NOP
  "00000000", -- 001000 NOP
  "00000000", -- 001001 NOP
  "00000000", -- 001010 NOP
  "00000000", -- 001011 NOP
  "00000000", -- 001100 NOP
  "00000000", -- 001101 NOP
  "00000000", -- 001110 NOP
  "00000000", -- 001111 NOP
  "00000000", -- 010000 NOP
  "00000000", -- 010001 NOP
  "00000000", -- 010010 NOP
  "00000000", -- 010011 NOP
  "00000000", -- 010100 NOP
  "00000000", -- 010101 NOP
  "00000000", -- 010110 NOP
  "00000000", -- 010111 NOP
  "00000000", -- 011000 NOP
  "00000000", -- 011001 NOP
  "00000000", -- 011010 NOP
  "00000000", -- 011011 NOP
  "00000000", -- 011100 NOP
  "00000000", -- 011101 NOP
  "00000000", -- 011110 NOP
  "00000000", -- 011111 NOP
  "01100000", -- 100000 ADDI1 RS1,RD,INP1
  "01100011", -- 100001 SUBI1 RS1,RD,INP1
  "01100110", -- 100010 ANDI1 RS1,RD,INP1
  "01101001", -- 100011 ORI1 RS1,RD,INP1
  "01101100", -- 100100 ADDI2 RS1,RD,INP1
  "01101111", -- 100101 SUBI2 RS1,RD,INP1
  "01110010", -- 100110 ANDI2 RS1,RD,INP1
  "01110101", -- 100111 ORI2 RS1,RD,INP1
  "01111000", -- 101000 MOV RS1,RD,INP1
  "01111010", -- 101001 SREG1 RS1,RD,INP1
  "01111011", -- 101010 SREG2 RS1,RD,INP1
  "10000001", -- 101011 SMEM RS1,RD,INP1
  "10000100", -- 101100 LMEM1 RS1,RD,INP1
  "10000111", -- 101101 LMEM2 RS1,RD,INP1
  "00000000", -- 101110 NOP
  "00000000", -- 101111 NOP
  "00000000", -- 110000 NOP
  "00000000", -- 110001 NOP
  "00000000", -- 110010 NOP
  "00000000", -- 110011 NOP
  "00000000", -- 110100 NOP
  "00000000", -- 110101 NOP
  "00000000", -- 110110 NOP
  "00000000", -- 110111 NOP
  "00000000", -- 111000 NOP
  "00000000", -- 111001 NOP
  "00000000", -- 111010 NOP
  "00000000", -- 111011 NOP
  "00000000", -- 111100 NOP
  "00000000", -- 111101 NOP
  "00000000", -- 111110 NOP
  "00000000" -- 111111 NOP
  );

  -- Function memory  not used in this case because the two LSB of the function field are directly used to drive the ALU
  -- type function_tr is array (2 ** FUNCTION_SIZE downto 0) of std_logic_vector(OPCODE_SIZE + 1 downto 0);
  -- signal base_addr_tr : opcode_mem_tr := (
  -- -- used to translate the opcode to its base address in the memory
  -- -- initialized for OPCODE_SIZE to be on 6 bits
  -- X"00", -- 000000 NOP
  -- X"01", -- 000001 ADD - SUB - AND - OR
  -- X"00", -- 000010 NOP
  -- X"00", -- 000011 NOP
  -- X"00"
  -- );

  -- Assumption is that if a value 0 is needed is loaded in A or B depending on the
  -- instruction if it require a specific immediate, otherwise the first location of
  -- the register file is loaded in B as it is always 0

  -- Code Words
  -- "0000000000000", --0 op: 000000 NOP
  -- "1111100100111", --1 op: 000001 ADD
  -- "1111101100111", --2 op: 000001 SUB
  -- "1111110100111", --3 op: 000001 AND
  -- "1111111100111", --4 op: 000001 OR
  -- "0110100100111", --5 op: 100000 ADDI1 RS1,RD,INP1
  -- "0110101100111", --6 op: 100001 SUBI1 RS1,RD,INP1
  -- "0110110100111", --7 op: 100010 ANDI1 RS1,RD,INP1
  -- "0110111100111", --8 op: 100011 ORI1 RS1,RD,INP1
  -- "1011000100111", --9 op: 100100 ADDI2 RS1,RD,INP1
  -- "1011001100111", --10 op: 100101 SUBI2 RS1,RD,INP1
  -- "1011010100111", --11 op: 100110 ANDI2 RS1,RD,INP1
  -- "1011011100111", --12 op: 100111 ORI2 RS1,RD,INP1
  -- "1011100100111", --13 op: 101000 MOV RS1,RD,INP1
  -- "0010000100111", --14 op: 101001 SREG1 RS1,RD,INP1
  -- "0010100100111", --15 op: 101010 SREG2 RS1,RD,INP1
  -- "1110100101100", --16 op: 101011 SMEM RS1,RD,INP1
  -- "0110000110101", --17 op: 101100 LMEM1 RS1,RD,INP1
  -- "1011100110101", --18 op: 101101 LMEM2 RS1,RD,INP1

  -- ucode memory (could also implented in another module)
  type ucode_mem_layout is array (0 to ucode_mem_size - 1) of std_logic_vector(CW_SIZE - 1 downto 0);

  -- Codeword Structure
  -- ****************************************************************************
  -- * RF1 * RF2 * EN1 * SA * SB * ALU1 * ALU2 * EN2 * RM * WM * EN3 * S3 * WF1 *
  -- ****************************************************************************

  signal ucode_mem : ucode_mem_layout := (
  "0000000000000", -- uaddr: 00000000 op: 000000 NOP
  "0000000000000", -- uaddr: 00000001 op: 000000 NOP
  "0000000000000", -- uaddr: 00000010 op: 000000 NOP
  "1110000000000", -- uaddr: 00000011 op: 000001 ADD - SUB - AND - OR
  "0001000100000", -- uaddr: 00000100 op: 000001 ADD - SUB - AND - OR
  "0010000000111", -- uaddr: 00000101 op: 000001 ADD - SUB - AND - OR
  "0000000000000", -- uaddr: 00000110 op: 000010 NOP
  "0000000000000", -- uaddr: 00000111 op: 000010 NOP
  "0000000000000", -- uaddr: 00001000 op: 000010 NOP
  "0000000000000", -- uaddr: 00001001 op: 000011 NOP
  "0000000000000", -- uaddr: 00001010 op: 000011 NOP
  "0000000000000", -- uaddr: 00001011 op: 000011 NOP
  "0000000000000", -- uaddr: 00001100 op: 000100 NOP
  "0000000000000", -- uaddr: 00001101 op: 000100 NOP
  "0000000000000", -- uaddr: 00001110 op: 000100 NOP
  "0000000000000", -- uaddr: 00001111 op: 000101 NOP
  "0000000000000", -- uaddr: 00010000 op: 000101 NOP
  "0000000000000", -- uaddr: 00010001 op: 000101 NOP
  "0000000000000", -- uaddr: 00010010 op: 000110 NOP
  "0000000000000", -- uaddr: 00010011 op: 000110 NOP
  "0000000000000", -- uaddr: 00010100 op: 000110 NOP
  "0000000000000", -- uaddr: 00010101 op: 000111 NOP
  "0000000000000", -- uaddr: 00010110 op: 000111 NOP
  "0000000000000", -- uaddr: 00010111 op: 000111 NOP
  "0000000000000", -- uaddr: 00011000 op: 001000 NOP
  "0000000000000", -- uaddr: 00011001 op: 001000 NOP
  "0000000000000", -- uaddr: 00011010 op: 001000 NOP
  "0000000000000", -- uaddr: 00011011 op: 001001 NOP
  "0000000000000", -- uaddr: 00011100 op: 001001 NOP
  "0000000000000", -- uaddr: 00011101 op: 001001 NOP
  "0000000000000", -- uaddr: 00011110 op: 001010 NOP
  "0000000000000", -- uaddr: 00011111 op: 001010 NOP
  "0000000000000", -- uaddr: 00100000 op: 001010 NOP
  "0000000000000", -- uaddr: 00100001 op: 001011 NOP
  "0000000000000", -- uaddr: 00100010 op: 001011 NOP
  "0000000000000", -- uaddr: 00100011 op: 001011 NOP
  "0000000000000", -- uaddr: 00100100 op: 001100 NOP
  "0000000000000", -- uaddr: 00100101 op: 001100 NOP
  "0000000000000", -- uaddr: 00100110 op: 001100 NOP
  "0000000000000", -- uaddr: 00100111 op: 001101 NOP
  "0000000000000", -- uaddr: 00101000 op: 001101 NOP
  "0000000000000", -- uaddr: 00101001 op: 001101 NOP
  "0000000000000", -- uaddr: 00101010 op: 001110 NOP
  "0000000000000", -- uaddr: 00101011 op: 001110 NOP
  "0000000000000", -- uaddr: 00101100 op: 001110 NOP
  "0000000000000", -- uaddr: 00101101 op: 001111 NOP
  "0000000000000", -- uaddr: 00101110 op: 001111 NOP
  "0000000000000", -- uaddr: 00101111 op: 001111 NOP
  "0000000000000", -- uaddr: 00110000 op: 010000 NOP
  "0000000000000", -- uaddr: 00110001 op: 010000 NOP
  "0000000000000", -- uaddr: 00110010 op: 010000 NOP
  "0000000000000", -- uaddr: 00110011 op: 010001 NOP
  "0000000000000", -- uaddr: 00110100 op: 010001 NOP
  "0000000000000", -- uaddr: 00110101 op: 010001 NOP
  "0000000000000", -- uaddr: 00110110 op: 010010 NOP
  "0000000000000", -- uaddr: 00110111 op: 010010 NOP
  "0000000000000", -- uaddr: 00111000 op: 010010 NOP
  "0000000000000", -- uaddr: 00111001 op: 010011 NOP
  "0000000000000", -- uaddr: 00111010 op: 010011 NOP
  "0000000000000", -- uaddr: 00111011 op: 010011 NOP
  "0000000000000", -- uaddr: 00111100 op: 010100 NOP
  "0000000000000", -- uaddr: 00111101 op: 010100 NOP
  "0000000000000", -- uaddr: 00111110 op: 010100 NOP
  "0000000000000", -- uaddr: 00111111 op: 010101 NOP
  "0000000000000", -- uaddr: 01000000 op: 010101 NOP
  "0000000000000", -- uaddr: 01000001 op: 010101 NOP
  "0000000000000", -- uaddr: 01000010 op: 010110 NOP
  "0000000000000", -- uaddr: 01000011 op: 010110 NOP
  "0000000000000", -- uaddr: 01000100 op: 010110 NOP
  "0000000000000", -- uaddr: 01000101 op: 010111 NOP
  "0000000000000", -- uaddr: 01000110 op: 010111 NOP
  "0000000000000", -- uaddr: 01000111 op: 010111 NOP
  "0000000000000", -- uaddr: 01001000 op: 011000 NOP
  "0000000000000", -- uaddr: 01001001 op: 011000 NOP
  "0000000000000", -- uaddr: 01001010 op: 011000 NOP
  "0000000000000", -- uaddr: 01001010 op: 011001 NOP
  "0000000000000", -- uaddr: 01001011 op: 011001 NOP
  "0000000000000", -- uaddr: 01001100 op: 011001 NOP
  "0000000000000", -- uaddr: 01001101 op: 011010 NOP
  "0000000000000", -- uaddr: 01001110 op: 011010 NOP
  "0000000000000", -- uaddr: 01001111 op: 011010 NOP
  "0000000000000", -- uaddr: 01010000 op: 011011 NOP
  "0000000000000", -- uaddr: 01010001 op: 011011 NOP
  "0000000000000", -- uaddr: 01010010 op: 011011 NOP
  "0000000000000", -- uaddr: 01010011 op: 011100 NOP
  "0000000000000", -- uaddr: 01010100 op: 011100 NOP
  "0000000000000", -- uaddr: 01010101 op: 011100 NOP
  "0000000000000", -- uaddr: 01010110 op: 011101 NOP
  "0000000000000", -- uaddr: 01010111 op: 011101 NOP
  "0000000000000", -- uaddr: 01011000 op: 011101 NOP
  "0000000000000", -- uaddr: 01011001 op: 011110 NOP
  "0000000000000", -- uaddr: 01011010 op: 011110 NOP
  "0000000000000", -- uaddr: 01011011 op: 011110 NOP
  "0000000000000", -- uaddr: 01011100 op: 011111 NOP
  "0000000000000", -- uaddr: 01011101 op: 011111 NOP
  "0000000000000", -- uaddr: 01011110 op: 011111 NOP
  "0110000000000", -- uaddr: 01011111 op: 100000 ADDI1 RS1,RD,INP1
  "0000000100000", -- uaddr: 01100000 op: 100000 ADDI1 RS1,RD,INP1
  "0010000000111", -- uaddr: 01100001 op: 100000 ADDI1 RS1,RD,INP1
  "0110000000000", -- uaddr: 01100010 op: 100001 SUBI1 RS1,RD,INP1
  "0000001100000", -- uaddr: 01100011 op: 100001 SUBI1 RS1,RD,INP1
  "0010000000111", -- uaddr: 01100100 op: 100001 SUBI1 RS1,RD,INP1
  "0110000000000", -- uaddr: 01100101 op: 100010 ANDI1 RS1,RD,INP1
  "0000010100000", -- uaddr: 01100110 op: 100010 ANDI1 RS1,RD,INP1
  "0010000000111", -- uaddr: 01100111 op: 100010 ANDI1 RS1,RD,INP1
  "0110000000000", -- uaddr: 01101000 op: 100011 ORI1 RS1,RD,INP1
  "0000011100000", -- uaddr: 01101001 op: 100011 ORI1 RS1,RD,INP1
  "0010000000111", -- uaddr: 01101010 op: 100011 ORI1 RS1,RD,INP1
  "1010000000000", -- uaddr: 01101011 op: 100100 ADDI2 RS1,RD,INP2
  "0001100100000", -- uaddr: 01101100 op: 100100 ADDI2 RS1,RD,INP2
  "0010000000111", -- uaddr: 01101101 op: 100100 ADDI2 RS1,RD,INP2
  "1010000000000", -- uaddr: 01101110 op: 100101 SUBI2 RS1,RD,INP2
  "0001101100000", -- uaddr: 01101111 op: 100101 SUBI2 RS1,RD,INP2
  "0010000000111", -- uaddr: 01110000 op: 100101 SUBI2 RS1,RD,INP2
  "1010000000000", -- uaddr: 01110001 op: 100110 ANDI2 RS1,RD,INP2
  "0001110100000", -- uaddr: 01110010 op: 100110 ANDI2 RS1,RD,INP2
  "0010000000111", -- uaddr: 01110011 op: 100110 ANDI2 RS1,RD,INP2
  "1010000000000", -- uaddr: 01110100 op: 100111 ORI2 RS1,RD,INP2
  "0001111100000", -- uaddr: 01110101 op: 100111 ORI2 RS1,RD,INP2
  "0010000000111", -- uaddr: 01110110 op: 100111 ORI2 RS1,RD,INP2
  "1110000000000", -- uaddr: 01110111 op: 101000 MOV RS1,RD
  "0001000100000", -- uaddr: 01111000 op: 101000 MOV RS1,RD
  "0010000000111", -- uaddr: 01111001 op: 101000 MOV RS1,RD
  "0110000000000", -- uaddr: 01111010 op: 101001 SREG1 RS1,RD,INP1
  "0000000100000", -- uaddr: 01111011 op: 101001 SREG1 RS1,RD,INP1
  "0010000000111", -- uaddr: 01111100 op: 101001 SREG1 RS1,RD,INP1
  "1010000000000", -- uaddr: 01111101 op: 101010 SREG2 RS1,RD,INP2
  "0000100100000", -- uaddr: 01111110 op: 101010 SREG2 RS1,RD,INP2
  "0010000000111", -- uaddr: 01111111 op: 101010 SREG2 RS1,RD,INP2
  "1110000000000", -- uaddr: 10000000 op: 101011 SMEM RS1,RD,INP2
  "0001100100000", -- uaddr: 10000001 op: 101011 SMEM RS1,RD,INP2
  "0000000001100", -- uaddr: 10000010 op: 101011 SMEM RS1,RD,INP2
  "0110000000000", -- uaddr: 10000011 op: 101100 LMEM1 RS1,RD,INP1
  "0000000100000", -- uaddr: 10000100 op: 101100 LMEM1 RS1,RD,INP1
  "0010000010101", -- uaddr: 10000101 op: 101100 LMEM1 RS1,RD,INP1
  "1010000000000", -- uaddr: 10000110 op: 101101 LMEM2 RS1,RD,INP2
  "0001100100000", -- uaddr: 10000111 op: 101101 LMEM2 RS1,RD,INP2
  "0010000010101", -- uaddr: 10001000 op: 101101 LMEM2 RS1,RD,INP2
  "0000000000000", -- uaddr: 10001001 op: 101110 NOP
  "0000000000000", -- uaddr: 10001010 op: 101110 NOP
  "0000000000000", -- uaddr: 10001011 op: 101110 NOP
  "0000000000000", -- uaddr: 10001100 op: 101111 NOP
  "0000000000000", -- uaddr: 10001101 op: 101111 NOP
  "0000000000000", -- uaddr: 10001110 op: 101111 NOP
  "0000000000000", -- uaddr: 10001111 op: 110000 NOP
  "0000000000000", -- uaddr: 10010000 op: 110000 NOP
  "0000000000000", -- uaddr: 10010001 op: 110000 NOP
  "0000000000000", -- uaddr: 10010010 op: 110001 NOP
  "0000000000000", -- uaddr: 10010011 op: 110001 NOP
  "0000000000000", -- uaddr: 10010100 op: 110001 NOP
  "0000000000000", -- uaddr: 10010101 op: 110010 NOP
  "0000000000000", -- uaddr: 10010110 op: 110010 NOP
  "0000000000000", -- uaddr: 10010111 op: 110010 NOP
  "0000000000000", -- uaddr: 10011000 op: 110011 NOP
  "0000000000000", -- uaddr: 10011001 op: 110011 NOP
  "0000000000000", -- uaddr: 10011010 op: 110011 NOP
  "0000000000000", -- uaddr: 10011011 op: 110100 NOP
  "0000000000000", -- uaddr: 10011100 op: 110100 NOP
  "0000000000000", -- uaddr: 10011101 op: 110100 NOP
  "0000000000000", -- uaddr: 10011110 op: 110101 NOP
  "0000000000000", -- uaddr: 10011111 op: 110101 NOP
  "0000000000000", -- uaddr: 10100000 op: 110101 NOP
  "0000000000000", -- uaddr: 10100001 op: 110110 NOP
  "0000000000000", -- uaddr: 10100010 op: 110110 NOP
  "0000000000000", -- uaddr: 10100011 op: 110110 NOP
  "0000000000000", -- uaddr: 10100100 op: 110111 NOP
  "0000000000000", -- uaddr: 10100101 op: 110111 NOP
  "0000000000000", -- uaddr: 10100110 op: 110111 NOP
  "0000000000000", -- uaddr: 10100111 op: 111000 NOP
  "0000000000000", -- uaddr: 10101000 op: 111000 NOP
  "0000000000000", -- uaddr: 10101001 op: 111000 NOP
  "0000000000000", -- uaddr: 10101010 op: 111001 NOP
  "0000000000000", -- uaddr: 10101011 op: 111001 NOP
  "0000000000000", -- uaddr: 10101100 op: 111001 NOP
  "0000000000000", -- uaddr: 10101101 op: 111010 NOP
  "0000000000000", -- uaddr: 10101110 op: 111010 NOP
  "0000000000000", -- uaddr: 10101111 op: 111010 NOP
  "0000000000000", -- uaddr: 10110000 op: 111011 NOP
  "0000000000000", -- uaddr: 10110001 op: 111011 NOP
  "0000000000000", -- uaddr: 10110010 op: 111011 NOP
  "0000000000000", -- uaddr: 10110011 op: 111100 NOP
  "0000000000000", -- uaddr: 10110100 op: 111100 NOP
  "0000000000000", -- uaddr: 10110101 op: 111100 NOP
  "0000000000000", -- uaddr: 10110110 op: 111101 NOP
  "0000000000000", -- uaddr: 10110111 op: 111101 NOP
  "0000000000000", -- uaddr: 10111000 op: 111101 NOP
  "0000000000000", -- uaddr: 10111001 op: 111110 NOP
  "0000000000000", -- uaddr: 10111010 op: 111110 NOP
  "0000000000000", -- uaddr: 10111011 op: 111110 NOP
  "0000000000000", -- uaddr: 10111100 op: 111111 NOP
  "0000000000000", -- uaddr: 10111101 op: 111111 NOP
  "0000000000000" -- uaddr: 10111110 op: 111111 NOP
  );

  type STAR_type is (S0, S1);
  signal STAR                              : STAR_type;
  signal U_ADDR, next_u_addr, reloc_u_addr : std_logic_vector(integer(ceil(log2(real(ucode_mem_size)))) - 1 downto 0);
  signal COUNTER                           : std_logic_vector(1 downto 0);
  signal CONTROL_WORD                      : std_logic_vector(CW_SIZE - 1 downto 0);

begin

  -- The address relocation is to compute OPCODE * OP_CLOCK_NEEDED, this could be slow, thus a LUT is used to
  -- compute the correct address. After the relocation the address is extended to have a coherent address size for the
  -- ucode memory. For example if the pipeline stages (thus OP_CLOCK_NEEDED) were a power of 2 then only a shift is needed

  reloc_u_addr <= base_addr_tr(to_integer(unsigned(opcode)));

  -- The next address is computed here, it is only to describe that beetween the rising clock edges
  -- the adder is computing the next value. This could also be done inside the cloked process
  -- but by doing UADDR <= UADDR + 1
  next_u_addr <= std_logic_vector(unsigned(U_ADDR) + 1);

  -- The U_ADDR is used directly to address the umemory
  CONTROL_WORD <= ucode_mem(to_integer(unsigned(U_ADDR)));

  -- Codeword Structure
  -- ****************************************************************************
  -- * RF1 * RF2 * EN1 * S1 * S2 * ALU1 * ALU2 * EN2 * RM * WM * EN3 * S3 * WF1 *
  -- ****************************************************************************

  -- Stage 1
  rf_rd1 <= CONTROL_WORD(CW_SIZE - 1);
  rf_rd2 <= CONTROL_WORD(CW_SIZE - 2);
  en_1   <= CONTROL_WORD(CW_SIZE - 3);
  -- Stage 2
  s_a <= CONTROL_WORD(CW_SIZE - 4);
  s_b <= CONTROL_WORD(CW_SIZE - 5);
  -- As stated above the function bits are used directly to drive the  ALU unit to
  -- save memory space in the umemory. This is done only when the instruction is a R-Type
  -- OPCODE = 00 ... 0001;

  -- the last condition depends on which phase is the ALU
  -- in our case in the second stage
  alu_op <= func when opcode = RTYPE and STAR = S1 and to_integer(unsigned(COUNTER)) = 1 else
    CONTROL_WORD(CW_SIZE - 6 downto CW_SIZE - 7);
  en_2 <= CONTROL_WORD(CW_SIZE - 8);
  -- Stage 3
  mr   <= CONTROL_WORD(CW_SIZE - 9);
  mw   <= CONTROL_WORD(CW_SIZE - 10);
  en_3 <= CONTROL_WORD(CW_SIZE - 11);
  s_am <= CONTROL_WORD(CW_SIZE - 12);
  wrf  <= CONTROL_WORD(CW_SIZE - 13);

  process (clk)
  begin
    if rising_edge(clk) then
      if reset = '1' then
        STAR    <= S0;
        U_ADDR  <= (others => '0');
        COUNTER <= std_logic_vector(to_unsigned(OP_CLOCK_NEEDED - 1, COUNTER'length));
      else
        case STAR is
          when S0 =>
            -- Sample the new OPCODE and start a loop to sweep through 3 memory locations
            U_ADDR  <= reloc_u_addr;
            COUNTER <= std_logic_vector(to_unsigned(OP_CLOCK_NEEDED - 1, COUNTER'length));
            STAR    <= S1;

          when S1 =>
            -- Here we swees all the needed memory locations of the umemory
            -- to output the correct CW
            U_ADDR  <= next_u_addr;
            COUNTER <= std_logic_vector(unsigned(COUNTER) - 1);

            if to_integer(unsigned(COUNTER)) = 1 then
              STAR <= S0;
            else
              STAR <= S1;
            end if;

          when others =>
            STAR <= S0;
        end case;
      end if;
    end if;
  end process;

end architecture;