core: Implement LPCR register

This implements the LPCR (Logical Partition Control Register) with 5
read/write bits.  The other 59 bits are read-only; two (HR and UPRT)
read as 1 and the rest as 0.

The bits that are implemented are:
* HAIL - enables taking interrupts with relocation on
* LD - enables large decrementer mode
* HEIC - disables external interrupts when set
* LPES - controls how external interrupts are delivered
* HVICE - does nothing at present since there is no source of
  	  Hypervisor Virtualization Interrupts.

This also fixes a bug where MSR[RI] was getting cleared by the
delivery of hypervisor interrupts.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/443/head
Paul Mackerras 4 weeks ago
parent 63fff5e05c
commit 1b6ee631bc

@ -56,6 +56,7 @@ package common is
constant SPR_HSPRG1 : spr_num_t := 305;
constant SPR_PID : spr_num_t := 48;
constant SPR_PTCR : spr_num_t := 464;
constant SPR_LPCR : spr_num_t := 318;
constant SPR_PVR : spr_num_t := 287;
constant SPR_FSCR : spr_num_t := 153;
constant SPR_HFSCR : spr_num_t := 190;
@ -189,6 +190,7 @@ package common is
constant SPRSEL_LOGD : spr_selector := 4x"5";
constant SPRSEL_CFAR : spr_selector := 4x"6";
constant SPRSEL_FSCR : spr_selector := 4x"7";
constant SPRSEL_LPCR : spr_selector := 4x"8";
constant SPRSEL_HEIR : spr_selector := 4x"9";
constant SPRSEL_CTRL : spr_selector := 4x"a";
constant SPRSEL_DSCR : spr_selector := 4x"b";
@ -235,6 +237,15 @@ package common is
constant FPSCR_NI : integer := 63 - 61;
constant FPSCR_RN : integer := 63 - 63;

-- LPCR bit numbers
constant LPCR_HAIL : integer := 63 - 37;
constant LPCR_UPRT : integer := 63 - 41;
constant LPCR_HR : integer := 63 - 43;
constant LPCR_LD : integer := 63 - 46;
constant LPCR_HEIC : integer := 63 - 59;
constant LPCR_LPES : integer := 63 - 60;
constant LPCR_HVICE : integer := 63 - 62;

-- Real addresses
-- REAL_ADDR_BITS is the number of real address bits that we store
constant REAL_ADDR_BITS : positive := 56;
@ -301,6 +312,11 @@ package common is
dexcr_pro: aspect_bits_t;
hdexcr_hyp: aspect_bits_t;
hdexcr_enf: aspect_bits_t;
lpcr_hail: std_ulogic;
lpcr_ld: std_ulogic;
lpcr_heic: std_ulogic;
lpcr_lpes: std_ulogic;
lpcr_hvice: std_ulogic;
end record;
constant ctrl_t_init : ctrl_t :=
(wait_state => '0', run => '1', xer_low => 18x"0",
@ -308,6 +324,8 @@ package common is
dscr => (others => '0'),
dexcr_pnh => aspect_bits_init, dexcr_pro => aspect_bits_init,
hdexcr_hyp => aspect_bits_init, hdexcr_enf => aspect_bits_init,
lpcr_hail => '0', lpcr_ld => '1', lpcr_heic => '0',
lpcr_lpes => '0', lpcr_hvice => '0',
others => (others => '0'));

type timebase_ctrl is record
@ -778,6 +796,7 @@ package common is
write_xerc_enable : std_ulogic;
xerc : xer_common_t;
interrupt : std_ulogic;
alt_intr : std_ulogic;
hv_intr : std_ulogic;
is_scv : std_ulogic;
intr_vec : intr_vector_t;
@ -788,7 +807,6 @@ package common is
br_taken: std_ulogic;
abs_br: std_ulogic;
srr1: std_ulogic_vector(15 downto 0);
msr: std_ulogic_vector(63 downto 0);
end record;
constant Execute1ToWritebackInit : Execute1ToWritebackType :=
(valid => '0', instr_tag => instr_tag_init, rc => '0', mode_32bit => '0',
@ -796,11 +814,11 @@ package common is
write_xerc_enable => '0', xerc => xerc_init,
write_data => (others => '0'), write_cr_mask => (others => '0'),
write_cr_data => (others => '0'), write_reg => (others => '0'),
interrupt => '0', hv_intr => '0', is_scv => '0', intr_vec => 0,
interrupt => '0', alt_intr => '0', hv_intr => '0', is_scv => '0', intr_vec => 0,
redirect => '0', redir_mode => "0000",
last_nia => (others => '0'),
br_last => '0', br_taken => '0', abs_br => '0',
srr1 => (others => '0'), msr => (others => '0'));
srr1 => (others => '0'));

type Execute1ToFPUType is record
valid : std_ulogic;
@ -885,13 +903,14 @@ package common is
br_last : std_ulogic;
br_taken : std_ulogic;
interrupt : std_ulogic;
intr_vec : std_ulogic_vector(16 downto 0);
alt_intr : std_ulogic;
intr_vec : std_ulogic_vector(63 downto 0);
end record;
constant WritebackToFetch1Init : WritebackToFetch1Type :=
(redirect => '0', virt_mode => '0', priv_mode => '0', big_endian => '0',
mode_32bit => '0', redirect_nia => (others => '0'),
br_last => '0', br_taken => '0', br_nia => (others => '0'),
interrupt => '0', intr_vec => 17x"0");
interrupt => '0', alt_intr => '0', intr_vec => 64x"0");

type WritebackToRegisterFileType is record
write_reg : gspr_index_t;
@ -917,6 +936,7 @@ package common is
intr : std_ulogic;
hv_intr : std_ulogic;
scv_int : std_ulogic;
alt_int : std_ulogic;
srr1 : std_ulogic_vector(15 downto 0);
end record;


@ -327,6 +327,9 @@ begin
when 5x"0e" =>
isram := '0';
sel := SPRSEL_FSCR;
when 5x"0f" =>
isram := '0';
sel := SPRSEL_LPCR;
when 5x"10" =>
isram := '0';
sel := SPRSEL_HEIR;

@ -490,6 +490,8 @@ architecture behaviour of decode1 is
i.sel := SPRSEL_XER;
when SPR_FSCR =>
i.sel := SPRSEL_FSCR;
when SPR_LPCR =>
i.sel := SPRSEL_LPCR;
when SPR_HEIR =>
i.sel := SPRSEL_HEIR;
when SPR_CTRL =>

@ -92,6 +92,7 @@ architecture behaviour of execute1 is
mult_32s : std_ulogic;
write_fscr : std_ulogic;
write_ic : std_ulogic;
write_lpcr : std_ulogic;
write_heir : std_ulogic;
set_heir : std_ulogic;
write_ctrl : std_ulogic;
@ -210,6 +211,7 @@ architecture behaviour of execute1 is
signal valid_in : std_ulogic;
signal ctrl: ctrl_t := ctrl_t_init;
signal ctrl_tmp: ctrl_t := ctrl_t_init;
signal dec_sign: std_ulogic;
signal rotator_result: std_ulogic_vector(63 downto 0);
signal rotator_carry: std_ulogic;
signal logical_result: std_ulogic_vector(63 downto 0);
@ -408,6 +410,20 @@ architecture behaviour of execute1 is
return ret;
end;

function assemble_lpcr(c: ctrl_t) return std_ulogic_vector is
variable ret : std_ulogic_vector(63 downto 0);
begin
ret := (others => '0');
ret(LPCR_HAIL) := c.lpcr_hail;
ret(LPCR_UPRT) := '1';
ret(LPCR_HR) := '1';
ret(LPCR_LD) := c.lpcr_ld;
ret(LPCR_HEIC) := c.lpcr_heic;
ret(LPCR_LPES) := c.lpcr_lpes;
ret(LPCR_HVICE) := c.lpcr_hvice;
return ret;
end;

function assemble_ctrl(c: ctrl_t; msrpr: std_ulogic) return std_ulogic_vector is
variable ret : std_ulogic_vector(63 downto 0);
begin
@ -443,6 +459,15 @@ architecture behaviour of execute1 is
return ret;
end;

function assemble_dec(c: ctrl_t) return std_ulogic_vector is
begin
if c.lpcr_ld = '1' then
return c.dec;
else
return 32x"0" & c.dec(31 downto 0);
end if;
end;

-- Tell vivado to keep the hierarchy for the random module so that the
-- net names in the xdc file match.
attribute keep_hierarchy : string;
@ -582,6 +607,8 @@ begin
tb_next <= thi & tlo;
end process;

dec_sign <= (ctrl.dec(63) and ctrl.lpcr_ld) or (ctrl.dec(31) and not ctrl.lpcr_ld);

dbg_ctrl_out <= ctrl;
log_rd_addr <= ex2.log_addr_spr;

@ -782,6 +809,8 @@ begin
case dbg_spr_addr(3 downto 0) is
when SPRSEL_FSCR =>
dbg_spr_data <= assemble_fscr(ctrl);
when SPRSEL_LPCR =>
dbg_spr_data <= assemble_lpcr(ctrl);
when SPRSEL_HEIR =>
dbg_spr_data <= ctrl.heir;
when SPRSEL_CFAR =>
@ -1173,6 +1202,7 @@ begin
v.e.redir_mode := ex1.msr(MSR_IR) & not ex1.msr(MSR_PR) &
not ex1.msr(MSR_LE) & not ex1.msr(MSR_SF);
v.e.intr_vec := 16#700#;
v.e.alt_intr := ctrl.lpcr_hail and ex1.msr(MSR_IR) and ex1.msr(MSR_DR);
v.e.mode_32bit := not ex1.msr(MSR_SF);
v.e.instr_tag := e_in.instr_tag;
v.e.last_nia := e_in.nia;
@ -1441,6 +1471,8 @@ begin
v.se.write_cfar := '1';
when SPRSEL_FSCR =>
v.se.write_fscr := '1';
when SPRSEL_LPCR =>
v.se.write_lpcr := '1';
when SPRSEL_HEIR =>
v.se.write_heir := '1';
when SPRSEL_CTRL =>
@ -1659,7 +1691,8 @@ begin
v.busy := '0';
bypass_valid := actions.bypass_valid;

irq_valid := ex1.msr(MSR_EE) and (pmu_to_x.intr or ctrl.dec(63) or ext_irq_in);
irq_valid := ex1.msr(MSR_EE) and (pmu_to_x.intr or dec_sign or
(ext_irq_in and not ctrl.lpcr_heic));

if valid_in = '1' then
v.prev_op := e_in.insn_type;
@ -1701,14 +1734,14 @@ begin
if pmu_to_x.intr = '1' then
v.e.intr_vec := 16#f00#;
report "IRQ valid: PMU";
elsif ctrl.dec(63) = '1' then
elsif dec_sign = '1' then
v.e.intr_vec := 16#900#;
report "IRQ valid: DEC";
elsif ext_irq_in = '1' then
v.e.intr_vec := 16#500#;
report "IRQ valid: External";
v.ext_interrupt := '1';
v.e.hv_intr := '1';
v.e.hv_intr := not ctrl.lpcr_lpes;
end if;
v.e.srr1 := (others => '0');
exception := '1';
@ -1925,12 +1958,13 @@ begin
with ex1.spr_select.sel select spr_result <=
timebase when SPRSEL_TB,
32x"0" & timebase(63 downto 32) when SPRSEL_TBU,
ctrl.dec when SPRSEL_DEC,
assemble_dec(ctrl) when SPRSEL_DEC,
32x"0" & PVR_MICROWATT when SPRSEL_PVR,
log_wr_addr & ex2.log_addr_spr when SPRSEL_LOGA,
log_rd_data when SPRSEL_LOGD,
ctrl.cfar when SPRSEL_CFAR,
assemble_fscr(ctrl) when SPRSEL_FSCR,
assemble_lpcr(ctrl) when SPRSEL_LPCR,
ctrl.heir when SPRSEL_HEIR,
assemble_ctrl(ctrl, ex1.msr(MSR_PR)) when SPRSEL_CTRL,
39x"0" & ctrl.dscr when SPRSEL_DSCR,
@ -2089,6 +2123,13 @@ begin
elsif ex1.se.write_ic = '1' then
ctrl_tmp.fscr_ic <= ex1.ic;
end if;
if ex1.se.write_lpcr = '1' then
ctrl_tmp.lpcr_hail <= ex1.e.write_data(LPCR_HAIL);
ctrl_tmp.lpcr_ld <= ex1.e.write_data(LPCR_LD);
ctrl_tmp.lpcr_heic <= ex1.e.write_data(LPCR_HEIC);
ctrl_tmp.lpcr_lpes <= ex1.e.write_data(LPCR_LPES);
ctrl_tmp.lpcr_hvice <= ex1.e.write_data(LPCR_HVICE);
end if;
if ex1.se.write_heir = '1' then
ctrl_tmp.heir <= ex1.e.write_data;
elsif ex1.se.set_heir = '1' then
@ -2117,7 +2158,7 @@ begin
-- pending exceptions clear any wait state
-- ex1.fp_exception_next is not tested because it is not possible to
-- get into wait state with a pending FP exception.
irq_exc := pmu_to_x.intr or ctrl.dec(63) or ext_irq_in;
irq_exc := pmu_to_x.intr or dec_sign or ext_irq_in;
if ex1.trace_next = '1' or irq_exc = '1' or interrupt_in.intr = '1' then
ctrl_tmp.wait_state <= '0';
end if;
@ -2130,11 +2171,13 @@ begin
ctrl_tmp.msr(MSR_FP) <= '0';
ctrl_tmp.msr(MSR_FE0) <= '0';
ctrl_tmp.msr(MSR_FE1) <= '0';
ctrl_tmp.msr(MSR_IR) <= '0';
ctrl_tmp.msr(MSR_DR) <= '0';
ctrl_tmp.msr(MSR_IR) <= interrupt_in.alt_int;
ctrl_tmp.msr(MSR_DR) <= interrupt_in.alt_int;
ctrl_tmp.msr(MSR_LE) <= '1';
if interrupt_in.scv_int = '0' then
ctrl_tmp.msr(MSR_EE) <= '0';
end if;
if interrupt_in.scv_int = '0' and interrupt_in.hv_intr = '0' then
ctrl_tmp.msr(MSR_RI) <= '0';
end if;
end if;
@ -2158,7 +2201,6 @@ begin

-- update outputs
e_out <= ex2.e;
e_out.msr <= msr_copy(ctrl.msr);

run_out <= ctrl.run;
terminate_out <= ex2.se.terminate;

@ -391,11 +391,11 @@ begin
v_int.next_nia := RESET_ADDRESS;
end if;
elsif w_in.interrupt = '1' then
v_int.next_nia := 47x"0" & w_in.intr_vec(16 downto 2) & "00";
v_int.next_nia := w_in.intr_vec(63 downto 2) & "00";
end if;
if rst /= '0' or w_in.interrupt = '1' then
v.req := '0';
v.virt_mode := '0';
v.virt_mode := w_in.alt_intr and not rst;
v.priv_mode := '1';
v.big_endian := '0';
v_int.mode_32bit := '0';

@ -552,7 +552,7 @@ static const char *fast_spr_names[] =
"lr", "ctr", "srr0", "srr1", "hsrr0", "hsrr1",
"sprg0", "sprg1", "sprg2", "sprg3",
"hsprg0", "hsprg1", "xer", "tar",
"fscr", "unused", "heir", "cfar",
"fscr", "lpcr", "heir", "cfar",
};

static const char *ldst_spr_names[] = {

@ -75,6 +75,7 @@ begin
variable hvi : std_ulogic;
variable scv : std_ulogic;
variable intr_page : std_ulogic_vector(4 downto 0);
variable intr_seg : std_ulogic_vector(1 downto 0);
begin
w_out <= WritebackToRegisterFileInit;
c_out <= WritebackToCrFileInit;
@ -98,6 +99,10 @@ begin

srr1 := (others => '0');
intr_page := 5x"0";
if e_in.alt_intr = '1' then
intr_page := 5x"4";
end if;
intr_seg := e_in.alt_intr & e_in.alt_intr;
scv := '0';
if e_in.interrupt = '1' then
vec := e_in.intr_vec;
@ -105,7 +110,11 @@ begin
hvi := e_in.hv_intr;
scv := e_in.is_scv;
if e_in.is_scv = '1' then
if e_in.alt_intr = '0' then
intr_page := 5x"17";
else
intr_page := 5x"3";
end if;
end if;
elsif l_in.interrupt = '1' then
vec := l_in.intr_vec;
@ -117,6 +126,7 @@ begin
interrupt_out.hv_intr <= hvi;
interrupt_out.srr1 <= srr1;
interrupt_out.scv_int <= scv;
interrupt_out.alt_int <= e_in.alt_intr;

if intr = '0' then
if e_in.write_enable = '1' then
@ -174,7 +184,8 @@ begin

-- Outputs to fetch1
f.interrupt := intr;
f.intr_vec := intr_page & std_ulogic_vector(to_unsigned(vec, 12));
f.alt_intr := e_in.alt_intr;
f.intr_vec := intr_seg & 45x"0" & intr_page & std_ulogic_vector(to_unsigned(vec, 12));
f.redirect := e_in.redirect;
f.redirect_nia := e_in.write_data;
f.br_nia := e_in.last_nia;

Loading…
Cancel
Save