-- 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 -- two multiplexer entities and behavioral 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; function "=" (left, right : std_logic_vector) return std_logic; function "/=" (left, right : std_logic_vector) return std_logic; -- 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; function "=" (left, right : std_logic_vector) return std_logic is variable result : std_logic := '1'; begin if left'length /= right'length then report "equal test on unequal lengths" severity warning; return '0'; end if; if (left'left>left'right)=(right'left>right'right) then for i in left'range loop if left(i) /= right((right'left-left'left)+i) then result := '0'; end if; end loop; else for i in left'range loop if left(i) /= right(right'high-(right'low-left'low+i)) then result := '0'; end if; end loop; end if; return result; end function "="; function "/=" (left, right : std_logic_vector) return std_logic is variable result : std_logic := '0'; begin if left'length /= right'length then report "not equal test on unequal lengths" severity warning; return '1'; end if; if (left'left>left'right)=(right'left>right'right) then for i in left'range loop if left(i) /= right((right'left-left'left)+i) then result := '1'; end if; end loop; else for i in left'range loop if left(i) /= right(right'high-(right'low-left'low+i)) then result := '1'; end if; end loop; end if; return result; end function "/="; 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 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 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; use WORK.util_pkg.all; 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); subtype word_5 is std_logic_vector(4 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 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 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 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 <= ??? 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 <= ??? 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); -- MEMWrite <= ??? MEMRead <= (MEM_IR(31 downto 26)="100011"); -- lw 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); MemtoReg <= (WB_IR(31 downto 26)="100011"); -- lw WB_write_enb <= (WB_IR(31 downto 26)="100011"); -- lw or ??? 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 done. 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