You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
microwatt/icache_tb.vhdl

163 lines
4.2 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
library work;
use work.common.all;
use work.wishbone_types.all;
entity icache_tb is
end icache_tb;
architecture behave of icache_tb is
signal clk : std_ulogic;
signal rst : std_ulogic;
signal i_out : Fetch1ToIcacheType;
signal i_in : IcacheToDecode1Type;
signal wb_bram_in : wishbone_master_out;
signal wb_bram_out : wishbone_slave_out;
constant clk_period : time := 10 ns;
begin
icache0: entity work.icache
generic map(
LINE_SIZE => 64,
NUM_LINES => 4
)
port map(
clk => clk,
rst => rst,
i_in => i_out,
i_out => i_in,
stall_in => '0',
flush_in => '0',
inval_in => '0',
wishbone_out => wb_bram_in,
wishbone_in => wb_bram_out
);
-- BRAM Memory slave
bram0: entity work.wishbone_bram_wrapper
generic map(
MEMORY_SIZE => 1024,
RAM_INIT_FILE => "icache_test.bin"
)
port map(
clk => clk,
rst => rst,
wishbone_in => wb_bram_in,
wishbone_out => wb_bram_out
);
clk_process: process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
rst_process: process
begin
rst <= '1';
wait for 2*clk_period;
rst <= '0';
wait;
end process;
stim: process
begin
i_out.req <= '0';
i_out.nia <= (others => '0');
i_out.stop_mark <= '0';
i_out.priv_mode <= '1';
i_out.virt_mode <= '0';
i_out.big_endian <= '0';
i_out.fetch_fail <= '0';
i_out.predicted <= '0';
i_out.pred_ntaken <= '0';
Add TLB to icache This adds a direct-mapped TLB to the icache, with 64 entries by default. Execute1 now sends a "virt_mode" signal from MSR[IR] to fetch1 along with redirects to indicate whether instruction addresses should be translated through the TLB, and fetch1 sends that on to icache. Similarly a "priv_mode" signal is sent to indicate the privilege mode for instruction fetches. This means that changes to MSR[IR] or MSR[PR] don't take effect until the next redirect, meaning an isync, rfid, branch, etc. The icache uses a hash of the effective address (i.e. next instruction address) to index the TLB. The hash is an XOR of three fields of the address; with a 64-entry TLB, the fields are bits 12--17, 18--23 and 24--29 of the address. TLB invalidations simply invalidate the indexed TLB entry without checking the contents. If the icache detects a TLB miss with virt_mode=1, it will send a fetch_failed indication through fetch2 to decode1, which will turn it into a special OP_FETCH_FAILED opcode with unit=LDST. That will get sent down to loadstore1 which will currently just raise a Instruction Storage Interrupt (0x400) exception. One bit in the PTE obtained from the TLB is used to check whether an instruction access is allowed -- the privilege bit (bit 3). If bit 3 is 1 and priv_mode=0, then a fetch_failed indication is sent down to fetch2 and to decode1, which generates an OP_FETCH_FAILED. Any PTEs with PTE bit 0 (EAA[3]) clear or bit 8 (R) clear should not be put into the iTLB since such PTEs would not allow execution by any context. Tlbie operations get sent from mmu to icache over a new connection. Unfortunately the privileged instruction tests are broken for now. Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
5 years ago
wait until rising_edge(clk);
wait until rising_edge(clk);
wait until rising_edge(clk);
i_out.next_nia <= x"0000000000000004";
i_out.next_rpn <= (others => '0');
wait until rising_edge(clk);
i_out.req <= '1';
i_out.nia <= x"0000000000000004";
i_out.rpn <= (others => '0');
wait for 30*clk_period;
wait until rising_edge(clk);
assert i_in.valid = '1' severity failure;
assert i_in.insn = x"00000001"
report "insn @" & to_hstring(i_out.nia) &
"=" & to_hstring(i_in.insn) &
" expected 00000001"
severity failure;
i_out.req <= '0';
i_out.next_nia <= x"0000000000000008";
wait until rising_edge(clk);
-- hit
i_out.req <= '1';
i_out.nia <= x"0000000000000008";
wait until rising_edge(clk);
wait until rising_edge(clk);
assert i_in.valid = '1' severity failure;
assert i_in.insn = x"00000002"
report "insn @" & to_hstring(i_out.nia) &
"=" & to_hstring(i_in.insn) &
" expected 00000002"
severity failure;
i_out.next_nia <= x"0000000000000040";
wait until rising_edge(clk);
-- another miss
i_out.req <= '1';
i_out.nia <= x"0000000000000040";
wait for 30*clk_period;
wait until rising_edge(clk);
assert i_in.valid = '1' severity failure;
assert i_in.insn = x"00000010"
report "insn @" & to_hstring(i_out.nia) &
"=" & to_hstring(i_in.insn) &
" expected 00000010"
severity failure;
-- test something that aliases
i_out.next_nia <= x"0000000000000100";
wait until rising_edge(clk);
i_out.req <= '1';
i_out.nia <= x"0000000000000100";
wait until rising_edge(clk);
wait until rising_edge(clk);
assert i_in.valid = '0' severity failure;
wait until rising_edge(clk);
wait for 30*clk_period;
wait until rising_edge(clk);
assert i_in.valid = '1' severity failure;
assert i_in.insn = x"00000040"
report "insn @" & to_hstring(i_out.nia) &
"=" & to_hstring(i_in.insn) &
" expected 00000040"
severity failure;
i_out.req <= '0';
std.env.finish;
end process;
end;