-- pipe2.vhdl VHDL '93 version using entities from WORK library -- basic five stage pipeline of MIPS architecture -- The 411 course pipeline has the same five stages -- IF Instruction Fetch includes PC and instruction memory -- ID Instruction Decode and registers -- EX Execution including the ALU Arithmetic Logic Unit -- MEM data Memory -- WB Write Back into registers -- -- The signal naming convention uses the stage as a prefix -- -- This self contained VHDL file defines: -- a package declaration and body that defines functions -- a 32 bit and a 5 bit register entity with clock and clear inputs -- an instruction memory entity and behavioral architecture -- the instructions may have different bit patterns from project -- a data memory entity and behavioral architecture -- a general register entity and behavioral architecture -- two multiplexer entities and behavioral architectures -- an ALU (incomplete) entity and schematic architecture -- the 32 bit adder, add32, is compiled separately in WORK -- a top level entity, pipe2, test bench -- the architecture, schematic layout of the top level entity -- the signals for interconnecting the entities -- a clock generator process -- the entities connected with signals (port maps) -- a print process that shows the registers in the pipeline each clock library IEEE; use IEEE.std_logic_1164.all; package util_pkg is function to_integer(sig : std_logic_vector) return integer; end package util_pkg; package body util_pkg is function to_integer(sig : std_logic_vector) return integer is variable num : integer := 0; -- descending sig as integer begin for i in sig'range loop if sig(i)='1' then num := num*2+1; else -- use anything other than '1' as '0' num := num*2; end if; end loop; -- i return num; end function to_integer; end package body util_pkg; library IEEE; use IEEE.std_logic_1164.all; entity register_32 is port(clk : in std_logic; clear : in std_logic; input : in std_logic_vector (31 downto 0); output : out std_logic_vector (31 downto 0) ); end entity register_32; architecture behavior of register_32 is begin -- behavior reg_32: process(clk, clear) begin if clear='1' then output <= (others=>'0'); elsif clk'event and clk='1' then output <= input after 250 ps; end if; end process reg_32; end architecture behavior; -- of register_32 library IEEE; use IEEE.std_logic_1164.all; entity register_5 is port(clk : in std_logic; clear : in std_logic; input : in std_logic_vector (4 downto 0); output : out std_logic_vector (4 downto 0) ); end entity register_5; architecture behavior of register_5 is begin -- behavior reg_5: process(clk, clear) begin if clear='1' then output <= (others=>'0'); elsif clk'event and clk='1' then output <= input after 250 ps; end if; end process reg_5; end architecture behavior; -- of register_5 library IEEE; use IEEE.std_logic_1164.all; use WORK.util_pkg.all; entity instruction_memory is port(addr : in std_logic_vector (31 downto 0); inst : out std_logic_vector (31 downto 0)); end entity instruction_memory; architecture behavior of instruction_memory is begin -- behavior inst_mem: process(addr) subtype word is std_logic_vector(31 downto 0); type mem_array is array(natural range <>) of word; variable memory: mem_array(0 to 15) := ("10001100000000010000000000000100", -- lw "10001100000000100000000000001000", -- lw "00000000000000000000000000000000", -- nop "00000000000000000000000000000000", -- nop "00000000001000100001100000100000", -- add "00000000011000100010000000100010", -- sub "00000000000000010010101111000001", -- sll "00000000000000100011010000000010", -- srl "00000000000000110011100000100100", -- cmpl "10101100000000010000000000001000", -- sw "00000000000000000000000000000000", -- nop "00000000000000000000000000000000", -- nop "00000000000000000000000000000000", -- nop "00000000000000000000000000000000", -- nop "00000000000000000000000000000000", -- nop "00000000000000000000000000000000"); -- nop variable word_addr : natural; -- byte addr/4 begin word_addr := to_integer(addr(5 downto 2)); -- small memory inst <= memory(word_addr) after 250 ps; end process inst_mem; end architecture behavior; -- of instruction_memory library IEEE; use IEEE.std_logic_1164.all; use WORK.util_pkg.all; entity data_memory is port(address : in std_logic_vector (31 downto 0); write_data : in std_logic_vector (31 downto 0); read_enable : in std_logic; -- from address write_enable : in std_logic; -- rising clock and enable write_clk : in std_logic; -- required to write read_data : out std_logic_vector (31 downto 0)); end entity data_memory; architecture behavior of data_memory is begin -- behavior data_mem: process(address, write_clk) subtype word is std_logic_vector(31 downto 0); type mem_array is array(natural range <>) of word; variable memory: mem_array(0 to 1023) := ("00010001000100010001000100010001", "00100010001000100010001000100010", "00110011001100110011001100110011", others=>(others=>'0')); variable word_addr : natural; -- byte addr/4 begin if read_enable='1' then word_addr := to_integer(address(11 downto 2)); -- small mem read_data <= memory(word_addr) after 250 ps; else read_data <= "00000000000000000000000000000000"; end if; end process data_mem; end architecture behavior; -- of data_memory library IEEE; use IEEE.std_logic_1164.all; use WORK.util_pkg.all; entity registers is port(read_reg_1 : in std_logic_vector (4 downto 0); -- address read_reg_2 : in std_logic_vector (4 downto 0); -- address write_reg : in std_logic_vector (4 downto 0); -- address write_data : in std_logic_vector (31 downto 0); write_enable : in std_logic; -- rising clock and enable write_clk : in std_logic; -- required to write read_data_1 : out std_logic_vector (31 downto 0); read_data_2 : out std_logic_vector (31 downto 0)); end entity registers; architecture behavior of registers is begin -- behavior reg: process(read_reg_1, read_reg_2, write_clk) subtype word is std_logic_vector(31 downto 0); type reg_mem_type is array (natural range <>) of word; variable reg_mem : reg_mem_type(0 to 31) := (others =>(others =>'0')); variable reg_addr : natural; begin if write_enable='1' and write_clk'active and write_clk='1' then reg_addr := to_integer(write_reg); reg_mem(reg_addr) := write_data; end if; read_data_1 <= reg_mem(to_integer(read_reg_1)); read_data_2 <= reg_mem(to_integer(read_reg_2)); -- signals updated after process exits end process reg; end architecture behavior; -- of registers library IEEE; use IEEE.std_logic_1164.all; entity mux_32 is port(in0 : in std_logic_vector (31 downto 0); in1 : in std_logic_vector (31 downto 0); ctl : in std_logic; result : out std_logic_vector (31 downto 0)); end entity mux_32; architecture behavior of mux_32 is begin -- behavior -- no process needed with concurrent statements result <= in1 when ctl='1' else in0 after 250 ps; end architecture behavior; -- of mux_32 library IEEE; use IEEE.std_logic_1164.all; entity mux_5 is port(in0 : in std_logic_vector (4 downto 0); in1 : in std_logic_vector (4 downto 0); ctl : in std_logic; result : out std_logic_vector (4 downto 0)); end entity mux_5; architecture behavior of mux_5 is begin -- behavior -- no process needed with concurrent statements result <= in1 when ctl='1' else in0 after 250 ps; end architecture behavior; -- of mux_5 library IEEE; use IEEE.std_logic_1164.all; entity equal6 is -- basically a 6-bit op code compare port(inst : in std_logic_vector(5 downto 0); test : in std_logic_vector(5 downto 0); equal : out std_logic); end entity equal6; architecture circuits of equal6 is begin -- circuits equal <= (inst(0) xnor test(0)) and (inst(1) xnor test(1)) and (inst(2) xnor test(2)) and (inst(3) xnor test(3)) and (inst(4) xnor test(4)) and (inst(5) xnor test(5)); end architecture circuits; -- of equal6 library IEEE; use IEEE.std_logic_1164.all; entity alu_32 is port(inA : in std_logic_vector (31 downto 0); inB : in std_logic_vector (31 downto 0); inst : in std_logic_vector (31 downto 0); result : out std_logic_vector (31 downto 0)); end entity alu_32; architecture schematic of alu_32 is signal cin : std_logic := '0'; signal cout : std_logic; begin -- schematic Just an adder, so far adder: entity WORK.add32 port map(a => inA, b => inB, cin => cin, sum => result, cout => cout); end architecture schematic; -- of alu_32 entity pipe2 is -- test bench end entity pipe2; library STD; use STD.textio.all; library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_textio.all; use WORK.util_pkg.all; architecture schematic of pipe2 is -- top level connection of entities -- signals used in top level architecture (the interconnections) subtype word_32 is std_logic_vector(31 downto 0); subtype word_5 is std_logic_vector(4 downto 0); subtype word_6 is std_logic_vector(5 downto 0); signal zero_32 : word_32 := (others=>'0'); -- 32 bits of zero signal zero : std_logic := '0'; -- one bit zero signal four_32 : word_32 := x"00000004"; -- four signal lwop: word_6 := "100011"; signal clear : std_logic := '1'; -- one shot clear signal clk : std_logic := '1'; -- master clock signal clk_bar : std_logic := '0'; -- complement of master clock signal counter : integer := 0; -- master clock counter, raising edge signal PC_next : word_32; -- next value of PC signal PC : word_32; -- Program Counter signal inst : word_32; -- instruction fetched signal ID_IR : word_32; -- ID Instruction Register signal ID_read_data_1 : word_32; -- ID Register read data 1 signal ID_read_data_2 : word_32; -- ID Register read data 2 signal ID_sign_ext : word_32; -- ID sign extension signal RegDst : std_logic := '0'; -- ID register destination ctl signal ID_rd : word_5; -- ID register destination alias ID_addr : std_logic_vector(15 downto 0) is ID_IR(15 downto 0); signal EX_IR : word_32; -- EX Instruction Register signal EX_A : word_32; -- EX data A signal EX_B : word_32; -- EX data B signal EX_C : word_32; -- EX data C signal EX_rd : word_5; -- EX register destination signal EX_aluB : word_32; -- EX into ALU B signal ALUSrc : std_logic := '1'; -- EX ALU B side source control signal EX_result : word_32; -- EX ALU output signal MEM_IR : word_32; -- MEM Inst Register signal MEM_addr : word_32; -- MEM address signal MEM_data : word_32; -- MEM write data signal MEM_read_data : word_32; -- MEM read data signal MEM_rd : word_5; -- MEM register destination signal MEMRead : std_logic := '1'; -- MEM enable read signal MEMWrite : std_logic := '0'; -- MEM enable write signal MEM_lwop : std_logic; signal WB_IR : word_32; -- WB Instruction Register signal WB_read : word_32; -- WB read data signal WB_pass : word_32; -- WB pass data signal WB_rd : word_5; -- WB register destination signal MemtoReg : std_logic := '1'; -- WB mux control signal WB_result : word_32; -- WB mux output signal WB_write_enb : std_logic := '1'; -- WB enable register write begin -- schematic of pipe2, top level architecture and test bench clock_gen: process(clk, clear) -- clock generator and one shot clear signal begin if clear='1' then -- happens only once clear <= '0' after 200 ps; elsif clear='0' then -- avoid time zero glitch clk <= not clk after 5 ns; -- 10 ns period end if; end process clock_gen; clk_bar <= not clk; -- for split phase registers -- IF, Instruction Fetch pipeline stage PC_reg: entity WORK.register_32 port map(clk, clear, PC_next, PC); PC_incr: entity WORK.add32 port map(PC, four_32, zero, PC_next, open); inst_mem: entity WORK.instruction_memory port map(PC, inst); -- ID, Instruction Decode and register stack pipeline stage ID_IR_reg: entity WORK.register_32 port map(clk, clear, inst, ID_IR); ID_regs: entity WORK.registers port map( read_reg_1 => ID_IR(25 downto 21), read_reg_2 => ID_IR(20 downto 16), write_reg => WB_rd, write_data => WB_result, write_enable => WB_write_enb, write_clk => clk_bar, read_data_1 => ID_read_data_1, read_data_2 => ID_read_data_2); ID_mux_rd: entity WORK.mux_5 port map(in0 => ID_IR(20 downto 16), in1 => ID_IR(15 downto 11), ctl => RegDst, result => ID_rd); ID_sign_ext(15 downto 0) <= ID_addr; -- just wiring ID_sign_ext(31 downto 16) <= (others => ID_IR(15)); -- EX, Execute pipeline stage EX_IR_reg : entity WORK.register_32 port map(clk, clear, ID_IR, EX_IR); EX_A_reg : entity WORK.register_32 port map(clk, clear, ID_read_data_1, EX_A); EX_B_reg : entity WORK.register_32 port map(clk, clear, ID_read_data_2, EX_B); EX_C_reg : entity WORK.register_32 port map(clk, clear, ID_sign_ext, EX_C); EX_rd_reg : entity WORK.register_5 port map(clk, clear, ID_rd, EX_rd); EX_mux1 : entity WORK.mux_32 port map(in0 => EX_B, in1 => EX_C, ctl => ALUSrc, result => EX_aluB ); ALU : entity WORK.alu_32 port map(inA => EX_A, inB => EX_aluB, inst => EX_IR, result=> EX_result); -- MEM Data Memory pipeline stage MEM_IR_reg : entity WORK.register_32 port map(clk, clear, EX_IR, MEM_IR); MEM_addr_reg: entity WORK.register_32 port map(clk, clear, EX_result, MEM_addr); MEM_data_reg: entity WORK.register_32 port map(clk, clear, EX_B, MEM_data); MEM_rd_reg : entity WORK.register_5 port map(clk, clear, EX_rd, MEM_rd); MEM_lw : entity WORK.equal6 port map(MEM_IR(31 downto 26), lwop, MEM_lwop); MEMRead <= MEM_lwop; -- MEMWrite <= -- based on swop data_mem : entity WORK.data_memory port map(address => MEM_addr, write_data => MEM_data, read_enable => MEMRead, write_enable => MEMWrite, write_clk => clk_bar, read_data => MEM_read_data); -- WB, Write Back pipeline stage WB_IR_reg : entity WORK.register_32 port map(clk, clear, MEM_IR, WB_IR); WB_read_reg: entity WORK.register_32 port map(clk, clear, MEM_read_data, WB_read); WB_pass_reg: entity WORK.register_32 port map(clk, clear, MEM_addr, WB_pass); WB_rd_reg : entity WORK.register_5 port map(clk, clear, MEM_rd, WB_rd); decode_mt : entity WORK.equal6 port map(WB_IR(31 downto 26), lwop, MemtoReg); WB_mux : entity WORK.mux_32 port map(in0 => WB_pass, in1 => WB_read, ctl => MemtoReg, result => WB_result ); printout: process -- used to show state of registers in pipeline variable my_line : LINE; -- not part of working circuit begin wait for 9.5 ns; write(my_line, string'("clock ")); write(my_line, counter); write(my_line, string'(" inst=")); hwrite(my_line, inst); write(my_line, string'(" PC =")); hwrite(my_line, PC); write(my_line, string'(" PCnext=")); hwrite(my_line, PC_next); writeline(output, my_line); write(my_line, string'("ID stage IR=")); hwrite(my_line, ID_IR); if (WB_write_enb='1') and (WB_rd/="00000") then write(my_line, string'(" write=")); hwrite(my_line, WB_result); write(my_line, string'(" into =")); hwrite(my_line, "000000000000000000000000000"&WB_rd); else write(my_line, string'(" ")); write(my_line, string'(" ")); end if; write(my_line, string'(" ")); write(my_line, string'(" rd=")); write(my_line, ID_rd); writeline(output, my_line); write(my_line, string'("EX stage IR=")); hwrite(my_line, EX_IR); write(my_line, string'(" EX_A =")); hwrite(my_line, EX_A); write(my_line, string'(" EX_B =")); hwrite(my_line, EX_B); write(my_line, string'(" EX_C =")); hwrite(my_line, EX_C); write(my_line, string'(" rd=")); write(my_line, EX_rd); writeline(output, my_line); write(my_line, string'("EX stage")); write(my_line, string'(" ")); write(my_line, string'("EX_aluB=")); hwrite(my_line, EX_aluB); write(my_line, string'(" EX_res=")); hwrite(my_line, EX_result); writeline(output, my_line); write(my_line, string'("MEM stage IR=")); hwrite(my_line, MEM_IR); write(my_line, string'(" addr =")); hwrite(my_line, MEM_addr); write(my_line, string'(" data =")); hwrite(my_line, MEM_data); if MEMread='1' then write(my_line, string'(" read =")); hwrite(my_line, MEM_read_data); elsif MEMWrite='1' then write(my_line, string'(" wrote=")); hwrite(my_line, MEM_data); else write(my_line, string'(" ")); end if; write(my_line, string'(" rd=")); write(my_line, MEM_rd); writeline(output, my_line); write(my_line, string'("WB stage IR=")); hwrite(my_line, WB_IR); write(my_line, string'(" read =")); hwrite(my_line, WB_read); write(my_line, string'(" pass =")); hwrite(my_line, WB_pass); write(my_line, string'(" result=")); hwrite(my_line, WB_result); write(my_line, string'(" rd=")); write(my_line, WB_rd); writeline(output, my_line); write(my_line, string'("control RegDst=")); write(my_line, RegDst); write(my_line, string'(" ALUSrc=")); write(my_line, ALUSrc); write(my_line, string'(" MemtoReg=")); write(my_line, MemtoReg); write(my_line, string'(" MEMRead=")); write(my_line, MEMRead); write(my_line, string'(" MEMWrite=")); write(my_line, MEMWrite); write(my_line, string'(" WB_write_enb=")); write(my_line, WB_write_enb); writeline(output, my_line); writeline(output, my_line); -- blank line counter <= counter+1; wait for 0.5 ns; end process printout; end architecture schematic; -- of pipe2