mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-04-03 20:12:55 +00:00
core: Implement big-endian mode
Big-endian mode affects both instruction fetches and data accesses. For instruction fetches, we byte-swap each word read from memory when writing it into the icache data RAM, and use a tag bit to indicate whether each cache line contains instructions in BE or LE form. For data accesses, we simply need to invert the existing byte_reverse signal in BE mode. The only thing to be careful of is to get the sign bit from the correct place when doing a sign-extending load that crosses two doublewords of memory. For now, interrupts unconditionally set MSR[LE]. We will need some sort of interrupt-little-endian bit somewhere, perhaps in LPCR. This also fixes a debug report statement in fetch1.vhdl. Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
This commit is contained in:
@@ -108,6 +108,7 @@ package common is
|
||||
req: std_ulogic;
|
||||
virt_mode : std_ulogic;
|
||||
priv_mode : std_ulogic;
|
||||
big_endian : std_ulogic;
|
||||
stop_mark: std_ulogic;
|
||||
sequential: std_ulogic;
|
||||
nia: std_ulogic_vector(63 downto 0);
|
||||
@@ -245,10 +246,12 @@ package common is
|
||||
redirect: std_ulogic;
|
||||
virt_mode: std_ulogic;
|
||||
priv_mode: std_ulogic;
|
||||
big_endian: std_ulogic;
|
||||
redirect_nia: std_ulogic_vector(63 downto 0);
|
||||
end record;
|
||||
constant Execute1ToFetch1Init : Execute1ToFetch1Type := (redirect => '0', virt_mode => '0',
|
||||
priv_mode => '0', others => (others => '0'));
|
||||
priv_mode => '0', big_endian => '0',
|
||||
others => (others => '0'));
|
||||
|
||||
type Execute1ToLoadstore1Type is record
|
||||
valid : std_ulogic;
|
||||
|
||||
@@ -496,9 +496,10 @@ begin
|
||||
v.terminate := '0';
|
||||
icache_inval <= '0';
|
||||
v.busy := '0';
|
||||
-- send MSR[IR] and ~MSR[PR] up to fetch1
|
||||
-- send MSR[IR], ~MSR[PR] and ~MSR[LE] up to fetch1
|
||||
v.f.virt_mode := ctrl.msr(MSR_IR);
|
||||
v.f.priv_mode := not ctrl.msr(MSR_PR);
|
||||
v.f.big_endian := not ctrl.msr(MSR_LE);
|
||||
|
||||
-- Next insn adder used in a couple of places
|
||||
next_nia := std_ulogic_vector(unsigned(e_in.nia) + 4);
|
||||
@@ -740,6 +741,7 @@ begin
|
||||
when OP_RFID =>
|
||||
v.f.virt_mode := a_in(MSR_IR) or a_in(MSR_PR);
|
||||
v.f.priv_mode := not a_in(MSR_PR);
|
||||
v.f.big_endian := not a_in(MSR_LE);
|
||||
-- Can't use msr_copy here because the partial function MSR
|
||||
-- bits should be left unchanged, not zeroed.
|
||||
ctrl_tmp.msr(63 downto 31) <= a_in(63 downto 31);
|
||||
@@ -1161,6 +1163,8 @@ begin
|
||||
v.f.redirect := '1';
|
||||
v.f.virt_mode := '0';
|
||||
v.f.priv_mode := '1';
|
||||
-- XXX need an interrupt LE bit here, e.g. from LPCR
|
||||
v.f.big_endian := '0';
|
||||
end if;
|
||||
|
||||
if v.f.redirect = '1' then
|
||||
@@ -1176,7 +1180,7 @@ begin
|
||||
lv.data := c_in;
|
||||
lv.write_reg := gspr_to_gpr(e_in.write_reg);
|
||||
lv.length := e_in.data_len;
|
||||
lv.byte_reverse := e_in.byte_reverse;
|
||||
lv.byte_reverse := e_in.byte_reverse xnor ctrl.msr(MSR_LE);
|
||||
lv.sign_extend := e_in.sign_extend;
|
||||
lv.update := e_in.update;
|
||||
lv.update_reg := gspr_to_gpr(e_in.read_reg1);
|
||||
|
||||
@@ -50,8 +50,9 @@ begin
|
||||
log_nia <= r.nia(63) & r.nia(43 downto 2);
|
||||
if r /= r_next then
|
||||
report "fetch1 rst:" & std_ulogic'image(rst) &
|
||||
" IR:" & std_ulogic'image(e_in.virt_mode) &
|
||||
" P:" & std_ulogic'image(e_in.priv_mode) &
|
||||
" IR:" & std_ulogic'image(r_next.virt_mode) &
|
||||
" P:" & std_ulogic'image(r_next.priv_mode) &
|
||||
" E:" & std_ulogic'image(r_next.big_endian) &
|
||||
" R:" & std_ulogic'image(e_in.redirect) & std_ulogic'image(d_in.redirect) &
|
||||
" S:" & std_ulogic'image(stall_in) &
|
||||
" T:" & std_ulogic'image(stop_in) &
|
||||
@@ -81,11 +82,13 @@ begin
|
||||
end if;
|
||||
v.virt_mode := '0';
|
||||
v.priv_mode := '1';
|
||||
v.big_endian := '0';
|
||||
v_int.stop_state := RUNNING;
|
||||
elsif e_in.redirect = '1' then
|
||||
v.nia := e_in.redirect_nia(63 downto 2) & "00";
|
||||
v.virt_mode := e_in.virt_mode;
|
||||
v.priv_mode := e_in.priv_mode;
|
||||
v.big_endian := e_in.big_endian;
|
||||
elsif d_in.redirect = '1' then
|
||||
v.nia := d_in.redirect_nia(63 downto 2) & "00";
|
||||
elsif stall_in = '0' then
|
||||
|
||||
27
icache.vhdl
27
icache.vhdl
@@ -98,7 +98,8 @@ architecture rtl of icache is
|
||||
-- SET_SIZE_BITS is the log base 2 of the set size
|
||||
constant SET_SIZE_BITS : natural := LINE_OFF_BITS + INDEX_BITS;
|
||||
-- TAG_BITS is the number of bits of the tag part of the address
|
||||
constant TAG_BITS : natural := REAL_ADDR_BITS - SET_SIZE_BITS;
|
||||
-- the +1 is to allow the endianness to be stored in the tag
|
||||
constant TAG_BITS : natural := REAL_ADDR_BITS - SET_SIZE_BITS + 1;
|
||||
-- WAY_BITS is the number of bits to select a way
|
||||
constant WAY_BITS : natural := log2(NUM_WAYS);
|
||||
|
||||
@@ -289,9 +290,10 @@ architecture rtl of icache is
|
||||
end;
|
||||
|
||||
-- Get the tag value from the address
|
||||
function get_tag(addr: std_ulogic_vector(REAL_ADDR_BITS - 1 downto 0)) return cache_tag_t is
|
||||
function get_tag(addr: std_ulogic_vector(REAL_ADDR_BITS - 1 downto 0);
|
||||
endian: std_ulogic) return cache_tag_t is
|
||||
begin
|
||||
return addr(REAL_ADDR_BITS - 1 downto SET_SIZE_BITS);
|
||||
return endian & addr(REAL_ADDR_BITS - 1 downto SET_SIZE_BITS);
|
||||
end;
|
||||
|
||||
-- Read a tag from a tag memory row
|
||||
@@ -327,9 +329,9 @@ begin
|
||||
report "geometry bits don't add up" severity FAILURE;
|
||||
assert (LINE_OFF_BITS = ROW_OFF_BITS + ROW_LINEBITS)
|
||||
report "geometry bits don't add up" severity FAILURE;
|
||||
assert (REAL_ADDR_BITS = TAG_BITS + INDEX_BITS + LINE_OFF_BITS)
|
||||
assert (REAL_ADDR_BITS + 1 = TAG_BITS + INDEX_BITS + LINE_OFF_BITS)
|
||||
report "geometry bits don't add up" severity FAILURE;
|
||||
assert (REAL_ADDR_BITS = TAG_BITS + ROW_BITS + ROW_OFF_BITS)
|
||||
assert (REAL_ADDR_BITS + 1 = TAG_BITS + ROW_BITS + ROW_OFF_BITS)
|
||||
report "geometry bits don't add up" severity FAILURE;
|
||||
|
||||
sim_debug: if SIM generate
|
||||
@@ -359,6 +361,7 @@ begin
|
||||
signal wr_addr : std_ulogic_vector(ROW_BITS-1 downto 0);
|
||||
signal dout : cache_row_t;
|
||||
signal wr_sel : std_ulogic_vector(ROW_SIZE-1 downto 0);
|
||||
signal wr_dat : std_ulogic_vector(wishbone_in.dat'left downto 0);
|
||||
begin
|
||||
way: entity work.cache_ram
|
||||
generic map (
|
||||
@@ -372,10 +375,20 @@ begin
|
||||
rd_data => dout,
|
||||
wr_sel => wr_sel,
|
||||
wr_addr => wr_addr,
|
||||
wr_data => wishbone_in.dat
|
||||
wr_data => wr_dat
|
||||
);
|
||||
process(all)
|
||||
variable j: integer;
|
||||
begin
|
||||
-- byte-swap read data if big endian
|
||||
if r.store_tag(TAG_BITS - 1) = '0' then
|
||||
wr_dat <= wishbone_in.dat;
|
||||
else
|
||||
for i in 0 to (wishbone_in.dat'length / 8) - 1 loop
|
||||
j := ((i / 4) * 4) + (3 - (i mod 4));
|
||||
wr_dat(i * 8 + 7 downto i * 8) <= wishbone_in.dat(j * 8 + 7 downto j * 8);
|
||||
end loop;
|
||||
end if;
|
||||
do_read <= not (stall_in or use_previous);
|
||||
do_write <= '0';
|
||||
if wishbone_in.ack = '1' and replace_way = i then
|
||||
@@ -494,7 +507,7 @@ begin
|
||||
-- Extract line, row and tag from request
|
||||
req_index <= get_index(i_in.nia);
|
||||
req_row <= get_row(i_in.nia);
|
||||
req_tag <= get_tag(real_addr);
|
||||
req_tag <= get_tag(real_addr, i_in.big_endian);
|
||||
|
||||
-- Calculate address of beginning of cache row, will be
|
||||
-- used for cache miss processing if needed
|
||||
|
||||
@@ -201,14 +201,20 @@ begin
|
||||
end loop;
|
||||
|
||||
-- Work out the sign bit for sign extension.
|
||||
-- Assumes we are not doing both sign extension and byte reversal,
|
||||
-- in that for unaligned loads crossing two dwords we end up
|
||||
-- using a bit from the second dword, whereas for a byte-reversed
|
||||
-- (i.e. big-endian) load the sign bit would be in the first dword.
|
||||
negative := (r.length(3) and data_permuted(63)) or
|
||||
(r.length(2) and data_permuted(31)) or
|
||||
(r.length(1) and data_permuted(15)) or
|
||||
(r.length(0) and data_permuted(7));
|
||||
-- For unaligned loads crossing two dwords, the sign bit is in the
|
||||
-- first dword for big-endian (byte_reverse = 1), or the second dword
|
||||
-- for little-endian.
|
||||
if r.dwords_done = '1' and r.byte_reverse = '1' then
|
||||
negative := (r.length(3) and r.load_data(63)) or
|
||||
(r.length(2) and r.load_data(31)) or
|
||||
(r.length(1) and r.load_data(15)) or
|
||||
(r.length(0) and r.load_data(7));
|
||||
else
|
||||
negative := (r.length(3) and data_permuted(63)) or
|
||||
(r.length(2) and data_permuted(31)) or
|
||||
(r.length(1) and data_permuted(15)) or
|
||||
(r.length(0) and data_permuted(7));
|
||||
end if;
|
||||
|
||||
-- trim and sign-extend
|
||||
for i in 0 to 7 loop
|
||||
|
||||
Reference in New Issue
Block a user