-- part1c_start.vhdl VHDL '93 version using components and configuration -- 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 -- a 32 bit adder entity and behavioral architecture -- 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 and configuration -- a top level entity, part1c, test bench -- the architecture, schematic layout of the top level entity -- component interfaces used in schematic -- the signals for interconnecting the components -- a clock generator process -- the components connected with signals (port maps) -- a memory read process that reads "part1c.abs" -- a print process that shows the registers in the pipeline each clock -- the configuration that maps entities to components 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; -- 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 registers 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 -- use anything other than '1' as '0' 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 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 "="; 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='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='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; entity add_32 is port(a : in std_logic_vector (31 downto 0); b : in std_logic_vector (31 downto 0); cin : in std_logic; sum : out std_logic_vector (31 downto 0); cout : out std_logic); end entity add_32; architecture behavior of add_32 is begin -- behavior adder: process variable carry : std_logic; -- internal variable isum : std_logic_vector(31 downto 0); -- internal begin carry := cin; for i in 0 to 31 loop isum(i) := a(i) xor b(i) xor carry; carry := (a(i) and b(i)) or (a(i) and carry) or (b(i) and carry); end loop; sum <= isum; cout <= carry; wait for 1 ns; -- signals updated after process ends end process adder; end architecture behavior; -- of add_32 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) variable word_addr : natural; -- byte addr/4 begin word_addr := to_integer(addr(30 downto 0))/4; word_addr := word_addr mod 4096; -- crop to 12 bits 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) variable word_addr : natural; -- byte addr/4 begin if write_enable='1' and write_clk='1' then word_addr := to_integer(address(30 downto 0))/4; word_addr := word_addr mod 4096; -- crop to 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(30 downto 0))/4; word_addr := word_addr mod 4096; -- crop to 12 bits read_data <= memory(word_addr) after 250 ps; -- read memory else read_data <= x"00000000"; 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); 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 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 component add_32 port(a : in std_logic_vector (31 downto 0); b : in std_logic_vector (31 downto 0); cin : in std_logic; sum : out std_logic_vector (31 downto 0); cout : out std_logic); end component add_32; signal cin : std_logic := '0'; signal cout : std_logic; begin -- schematic adder: add_32 port map(a => inA, b => inB, cin => cin, sum => result, cout => cout); end architecture schematic; -- of alu_32 configuration alu_32_config of alu_32 is for schematic for adder : add_32 use entity WORK.add_32(behavior); end for; end for; end configuration alu_32_config; entity part1c is -- test bench end part1c; 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 part1c is -- top level connection of components -- selection of architectures for components -- is in part1c_config, below. -- components used in top level architecture (just the interface) component register_32 -- port copied from entity declaration 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 component register_32; component register_5 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 component register_5; component add_32 port(a : in std_logic_vector (31 downto 0); b : in std_logic_vector (31 downto 0); cin : in std_logic; sum : out std_logic_vector (31 downto 0); cout : out std_logic); end component add_32; component instruction_memory port(addr : in std_logic_vector (31 downto 0); inst : out std_logic_vector (31 downto 0)); end component instruction_memory; component data_memory 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 component data_memory; component registers 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 component registers; component mux_32 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 component mux_32; component mux_5 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 component mux_5; component alu_32 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 component alu_32; -- 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 := '0'; -- master clock signal clk_bar : std_logic := '1'; -- 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 part1c, top level architecture and test bench clock_gen: process(clk) -- clock generator and one shot clear signal begin if clear='1' then -- happens only once clear <= '0' after 200 ps; end if; clk <= not clk after 5 ns; -- 10 ns period end process clock_gen; clk_bar <= not clk; -- for split phase registers -- IF, Instruction Fetch pipeline stage PC_reg: register_32 port map(clk, clear, PC_next, PC); PC_incr: add_32 port map(PC, four_32, zero, PC_next, nc1); inst_mem: instruction_memory port map(PC, inst); -- ID, Instruction Decode and register stack pipeline stage ID_IR_reg: register_32 port map(clk, clear, inst, ID_IR); ID_regs: 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: 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 : register_32 port map(clk, clear, ID_IR, EX_IR); EX_A_reg : register_32 port map(clk, clear, ID_read_data_1, EX_A); EX_B_reg : register_32 port map(clk, clear, ID_read_data_2, EX_B); EX_C_reg : register_32 port map(clk, clear, ID_sign_ext, EX_C); EX_rd_reg : register_5 port map(clk, clear, ID_rd, EX_rd); -- ALUSrc <= ??? EX_mux1 : mux_32 port map(in0 => EX_B, in1 => EX_C, ctl => ALUSrc, result => EX_aluB ); ALU : alu_32 port map(inA => EX_A, inB => EX_aluB, inst => EX_IR, result=> EX_result); -- MEM Data Memory pipeline stage MEM_IR_reg : register_32 port map(clk, clear, EX_IR, MEM_IR); MEM_addr_reg: register_32 port map(clk, clear, EX_result, MEM_addr); MEM_data_reg: register_32 port map(clk, clear, EX_B, MEM_data); MEM_rd_reg : register_5 port map(clk, clear, EX_rd, MEM_rd); -- MEMWrite <= ??? MEMRead <= (MEM_IR(31 downto 26)="100011"); -- lw data_mem : 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 : register_32 port map(clk, clear, MEM_IR, WB_IR); WB_read_reg: register_32 port map(clk, clear, MEM_read_data, WB_read); WB_pass_reg: register_32 port map(clk, clear, MEM_addr, WB_pass); WB_rd_reg : 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 : mux_32 port map(in0 => WB_pass, in1 => WB_read, ctl => MemtoReg, result => WB_result ); loadmem: process -- read part1c.abs into shared memory array file my_input : TEXT open READ_MODE is "part1c.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'("loadmem process reading file")); writeline(output, my_line); while good loop exit when endfile(my_input); readline(my_input, my_input_line); hread(my_input_line, loc, good); exit when not good; hread(my_input_line, val, good); exit when not good; write(my_line, loc); write(my_line, string'(" =loc ")); write(my_line, val); write(my_line, string'(" =val")); writeline(output, my_line); word_addr := to_integer(loc(30 downto 0))/4; word_addr := word_addr mod 4096; -- 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; -- do not let it keep restarting process end process loadmem; printout: process(clk) -- used to show state of registers in pipeline variable my_line : LINE; -- not part of working circuit begin if clk='1' then 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 ")); hwrite(my_line, reg_mem(0)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(1)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(2)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(3)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(4)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(5)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(6)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(7)); writeline(output, my_line); write(my_line, string'(" 8-15 ")); hwrite(my_line, reg_mem(8)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(9)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(10)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(11)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(12)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(13)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(14)); write(my_line, string'(" ")); hwrite(my_line, reg_mem(15)); writeline(output, my_line); -- RAM memory write(my_line, string'("RAM 60- ")); hwrite(my_line, memory(24)); -- word at hex 60 byte address write(my_line, string'(" ")); hwrite(my_line, memory(25)); write(my_line, string'(" ")); hwrite(my_line, memory(26)); write(my_line, string'(" ")); hwrite(my_line, memory(27)); write(my_line, string'(" ")); hwrite(my_line, memory(28)); write(my_line, string'(" ")); hwrite(my_line, memory(29)); write(my_line, string'(" ")); hwrite(my_line, memory(30)); write(my_line, string'(" ")); hwrite(my_line, memory(31)); writeline(output, my_line); writeline(output, my_line); -- blank line counter <= counter+1; end if; end process printout; end architecture schematic; configuration part1c_config of part1c is use WORK.all; for schematic for all : register_32 use entity WORK.register_32; end for; for all : register_5 use entity WORK.register_5; end for; for inst_mem : instruction_memory use entity WORK.instruction_memory; end for; for data_mem : data_memory use entity WORK.data_memory; end for; for all : add_32 use entity WORK.add_32(behavior); end for; for all : registers -- use default end for; for all : mux_32 -- use default end for; for all : mux_5 -- use default end for; for alu : alu_32 use configuration WORK.alu_32_config; end for; end for; end configuration part1c_config;