-- part1_start.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 and memory -- a 32 bit and a 5 bit register entity with clock and clear inputs -- an instruction memory entity and behavioral architecture -- a data memory entity and behavioral architecture -- a general register entity and behavioral architecture -- multiplexer entities and behavioral architectures -- equal comparator entities and circuit architectures -- an incomplete ALU entity and schematic architecture -- a top level entity, part1, 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 memory read process that reads "part1.abs" -- 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; -- main memory, a process reads a file to load memory subtype word is std_logic_vector(31 downto 0); type mem_array is array(integer range <>) of word; shared variable memory: mem_array(0 to 4095); -- max 12 bit addresses -- general register memory type reg_mem_type is array (natural range <>) of word; shared variable reg_mem : reg_mem_type(0 to 31) := (others =>(others =>'0')); 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 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 -- only once 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 -- only once 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(clear : in std_logic; 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, clear) variable word_addr : natural; -- byte addr/4 begin if clear='1' then -- total machine clear inst <= x"00000000"; else -- normal operation word_addr := to_integer(addr(13 downto 2)); -- crop to 12 bits inst <= memory(word_addr) after 250 ps; end if; 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) variable word_addr : natural; -- byte addr/4 begin if write_enable='1' and write_clk='1' then word_addr := to_integer(address(13 downto 2)); -- 12 bits memory(word_addr) := write_data; -- write main memory read_data <= write_data; -- just something to output elsif read_enable='1' then word_addr := to_integer(address(13 downto 2)); -- 12 bits read_data <= memory(word_addr) after 250 ps; -- read memory else read_data <= x"00000000"; -- just to clean up printout 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) variable reg_addr : natural; begin if write_enable='1' and write_clk'active and write_clk='1' then reg_addr := to_integer(write_reg); if reg_addr/=0 then -- can not change register zero reg_mem(reg_addr) := write_data; end if; 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 mux32_3 is port(in0 : in std_logic_vector (31 downto 0); in1 : in std_logic_vector (31 downto 0); in2 : in std_logic_vector (31 downto 0); ct1 : in std_logic; -- pass in1(has priority) ct2 : in std_logic; -- pass in2 result : out std_logic_vector (31 downto 0)); end entity mux32_3; architecture behavior of mux32_3 is begin -- behavior -- no process needed with concurrent statements result <= in1 when ct1='1' else in2 when ct2='1' else in0 after 50 ps; end architecture behavior; -- of mux32_3 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 equal32 is -- a 32-bit compare port(inst : in std_logic_vector(31 downto 0); test : in std_logic_vector(31 downto 0); equal : out std_logic); end entity equal32; architecture circuits of equal32 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)) and (inst(6) xnor test(6)) and (inst(7) xnor test(7)) and (inst(8) xnor test(8)) and (inst(9) xnor test(9)) and (inst(10) xnor test(10)) and (inst(11) xnor test(11)) and (inst(12) xnor test(12)) and (inst(13) xnor test(13)) and (inst(14) xnor test(14)) and (inst(15) xnor test(15)) and (inst(16) xnor test(16)) and (inst(17) xnor test(17)) and (inst(18) xnor test(18)) and (inst(19) xnor test(19)) and (inst(20) xnor test(20)) and (inst(21) xnor test(21)) and (inst(22) xnor test(22)) and (inst(23) xnor test(23)) and (inst(24) xnor test(24)) and (inst(25) xnor test(25)) and (inst(26) xnor test(26)) and (inst(27) xnor test(27)) and (inst(28) xnor test(28)) and (inst(29) xnor test(29)) and (inst(30) xnor test(30)) and (inst(31) xnor test(31)); end architecture circuits; -- of equal32 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 equal5 is -- basically a 5-bit register number compare port(inst : in std_logic_vector(4 downto 0); test : in std_logic_vector(4 downto 0); equal : out std_logic); end entity equal5; architecture circuits of equal5 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)); end architecture circuits; -- of equal5 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 adder: entity WORK.add32 port map(a => inA, b => inB, cin => cin, sum => result, cout => cout); end architecture schematic; -- of alu_32 entity part1 is -- test bench end part1; 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 part1 is -- top level connection of entities -- signals used in top level architecture (the interconnections) subtype word_32 is std_logic_vector(31 downto 0); -- data and inst subtype word_6 is std_logic_vector(5 downto 0); -- op codes subtype word_5 is std_logic_vector(4 downto 0); -- register numbers 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"; -- lw op code signal RRop : word_6 := "000000"; -- RR op code 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 nc1 : std_logic; -- a No Connection for unused output 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 := '0'; -- MEM_IR is a lw op code 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 WB_rd_zero : std_logic; -- WB_rd is zero, no value 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 part1, 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, nc1); inst_mem: entity WORK.instruction_memory port map(clear, 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); -- RegDst must compute ??? 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); -- ALUSrc must compute ??? 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); compare_rd : entity WORK.equal5 port map(WB_rd, "00000", WB_rd_zero); WB_write_enb <= (not WB_rd_zero) and (MemtoReg ); -- or RRop or addiop ??? WB_mux : entity WORK.mux_32 port map(in0 => WB_pass, in1 => WB_read, ctl => MemtoReg, result => WB_result ); loadmem: process -- read part1.abs into shared memory array file my_input : TEXT open READ_MODE is "part1.abs"; -- hex data variable good : boolean := true; variable my_line : LINE; variable my_input_line : LINE; variable loc : std_logic_vector(31 downto 0); -- read from file variable val : std_logic_vector(31 downto 0); -- read from file variable word_addr : natural; -- byte addr/4 begin write(my_line, string' ("---PC--- --inst-- loadmem process reading .abs file")); writeline(output, my_line); while good loop exit when endfile(my_input); readline(my_input, my_input_line); my_line := new string'(my_input_line.all); -- for printing writeline(output, my_line); -- writing clears my_line hread(my_input_line, loc, good); exit when not good; hread(my_input_line, val, good); exit when not good; word_addr := to_integer(loc(13 downto 2)); -- crop to 12 bits memory(word_addr) := val; -- write main memory end loop; write(my_line, string'("loadmem complete. memory loaded")); writeline(output, my_line); wait; -- run once. do not keep restarting process end process loadmem; printout: process -- used to show pipeline, registers and memory variable my_line : LINE; -- not part of working circuit begin wait for 9.5 ns; -- just before rising clock 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); -- registers write(my_line, string'("reg 0-7 ")); for I in 0 to 7 loop hwrite(my_line, reg_mem(I)); write(my_line, string'(" ")); end loop; -- I writeline(output, my_line); write(my_line, string'(" 8-15 ")); for I in 8 to 15 loop hwrite(my_line, reg_mem(I)); write(my_line, string'(" ")); end loop; -- I writeline(output, my_line); write(my_line, string'(" 16-23 ")); for I in 16 to 23 loop hwrite(my_line, reg_mem(I)); write(my_line, string'(" ")); end loop; -- I writeline(output, my_line); -- RAM memory write(my_line, string'("RAM 70- ")); for I in 28 to 35 loop -- word at hex 70 byte address hwrite(my_line, memory(I)); write(my_line, string'(" ")); end loop; writeline(output, my_line); writeline(output, my_line); -- blank line counter <= counter+1; wait for 0.5 ns; -- rest of 10 ns clock period end process printout; end architecture schematic; -- of part1_start