Skip to content

Commit

Permalink
core: Implement two data watchpoints
Browse files Browse the repository at this point in the history
This implements the DAWR0, DAWRX0, DAWR1, and DAWRX1 registers, which
provide the ability to set watchpoints on two ranges of data addresses
and take an interrupt when an access is made to either range.

Signed-off-by: Paul Mackerras <[email protected]>
  • Loading branch information
paulusmack committed Jan 16, 2025
1 parent 09de073 commit e4f5fba
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 29 deletions.
4 changes: 4 additions & 0 deletions common.vhdl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ package common is
constant SPR_VRSAVE : spr_num_t := 256;
constant SPR_PIR : spr_num_t := 1023;
constant SPR_CIABR : spr_num_t := 187;
constant SPR_DAWR0 : spr_num_t := 180;
constant SPR_DAWR1 : spr_num_t := 181;
constant SPR_DAWRX0 : spr_num_t := 188;
constant SPR_DAWRX1 : spr_num_t := 189;

-- PMU registers
constant SPR_UPMC1 : spr_num_t := 771;
Expand Down
6 changes: 4 additions & 2 deletions decode2.vhdl
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,8 @@ begin
case decode_spr_num(d_in.insn) is
when SPR_XER =>
v.input_ov := '1';
when SPR_DAR | SPR_DSISR | SPR_PID | SPR_PTCR =>
when SPR_DAR | SPR_DSISR | SPR_PID | SPR_PTCR |
SPR_DAWR0 | SPR_DAWR1 | SPR_DAWRX0 | SPR_DAWRX1 =>
unit := LDST;
when SPR_TAR =>
v.e.uses_tar := '1';
Expand All @@ -499,7 +500,8 @@ begin
when SPR_XER =>
v.e.output_xer := '1';
v.output_ov := '1';
when SPR_DAR | SPR_DSISR | SPR_PID | SPR_PTCR =>
when SPR_DAR | SPR_DSISR | SPR_PID | SPR_PTCR |
SPR_DAWR0 | SPR_DAWR1 | SPR_DAWRX0 | SPR_DAWRX1 =>
unit := LDST;
if d_in.valid = '1' then
v.sgl_pipe := '1';
Expand Down
159 changes: 132 additions & 27 deletions loadstore1.vhdl
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ architecture behave of loadstore1 is
virt_mode : std_ulogic;
priv_mode : std_ulogic;
load_sp : std_ulogic;
sprsel : std_ulogic_vector(1 downto 0);
sprsel : std_ulogic_vector(2 downto 0);
ric : std_ulogic_vector(1 downto 0);
is_slbia : std_ulogic;
align_intr : std_ulogic;
dawr_intr : std_ulogic;
dword_index : std_ulogic;
two_dwords : std_ulogic;
incomplete : std_ulogic;
Expand All @@ -119,7 +120,8 @@ architecture behave of loadstore1 is
atomic_qw => '0', atomic_first => '0', atomic_last => '0',
rc => '0', nc => '0',
virt_mode => '0', priv_mode => '0', load_sp => '0',
sprsel => "00", ric => "00", is_slbia => '0', align_intr => '0',
sprsel => "000", ric => "00", is_slbia => '0', align_intr => '0',
dawr_intr => '0',
dword_index => '0', two_dwords => '0', incomplete => '0',
ea_valid => '0');

Expand All @@ -140,11 +142,15 @@ architecture behave of loadstore1 is
one_cycle : std_ulogic;
wr_sel : std_ulogic_vector(1 downto 0);
addr0 : std_ulogic_vector(63 downto 0);
sprsel : std_ulogic_vector(1 downto 0);
sprsel : std_ulogic_vector(2 downto 0);
dbg_spr : std_ulogic_vector(63 downto 0);
dbg_spr_ack: std_ulogic;
end record;

constant num_dawr : positive := 2;
type dawr_array_t is array(0 to num_dawr - 1) of std_ulogic_vector(63 downto 3);
type dawrx_array_t is array(0 to num_dawr - 1) of std_ulogic_vector(15 downto 0);

type reg_stage3_t is record
state : state_t;
complete : std_ulogic;
Expand All @@ -166,6 +172,10 @@ architecture behave of loadstore1 is
intr_vec : integer range 0 to 16#fff#;
srr1 : std_ulogic_vector(15 downto 0);
events : Loadstore1EventType;
dawr : dawr_array_t;
dawrx : dawrx_array_t;
dawr_uplim : dawr_array_t;
dawr_upd : std_ulogic;
end record;

signal req_in : request_t;
Expand Down Expand Up @@ -287,6 +297,25 @@ architecture behave of loadstore1 is
return fs2;
end;

function dawrx_match_enable(dawrx : std_ulogic_vector(15 downto 0); virt_mode : std_ulogic;
priv_mode : std_ulogic; is_store : std_ulogic)
return boolean is
begin
-- check PRIVM field; note priv_mode = '1' implies hypervisor mode
if (priv_mode = '0' and dawrx(0) = '0') or (priv_mode = '1' and dawrx(2) = '0') then
return false;
end if;
-- check WT/WTI fields
if dawrx(3) = '0' and virt_mode /= dawrx(4) then
return false;
end if;
-- check DW/DR fields
if (is_store = '0' and dawrx(5) = '0') or (is_store = '1' and dawrx(6) = '0') then
return false;
end if;
return true;
end;

begin
loadstore1_reg: process(clk)
begin
Expand All @@ -302,7 +331,7 @@ begin
r1.req.instr_fault <= '0';
r1.req.load <= '0';
r1.req.priv_mode <= '0';
r1.req.sprsel <= "00";
r1.req.sprsel <= "000";
r1.req.ric <= "00";
r1.req.xerc <= xerc_init;

Expand All @@ -313,7 +342,7 @@ begin
r2.req.instr_fault <= '0';
r2.req.load <= '0';
r2.req.priv_mode <= '0';
r2.req.sprsel <= "00";
r2.req.sprsel <= "000";
r2.req.ric <= "00";
r2.req.xerc <= xerc_init;

Expand All @@ -330,13 +359,25 @@ begin
r3.stage1_en <= '1';
r3.events.load_complete <= '0';
r3.events.store_complete <= '0';
for i in 0 to num_dawr - 1 loop
r3.dawr(i) <= (others => '0');
r3.dawrx(i) <= (others => '0');
r3.dawr_uplim(i) <= (others => '0');
end loop;
r3.dawr_upd <= '0';
flushing <= '0';
else
r1 <= r1in;
r2 <= r2in;
r3 <= r3in;
flushing <= (flushing or (r1in.req.valid and r1in.req.align_intr)) and
flushing <= (flushing or (r1in.req.valid and
(r1in.req.align_intr or r1in.req.dawr_intr))) and
not flush;
report "r2.1cyc<=" & std_ulogic'image(r2in.one_cycle) & " r2req.dawr<=" &
std_ulogic'image(r2in.req.dawr_intr) & " r2req.valid<=" & std_ulogic'image(r2in.req.valid) &
" r2req.dc_req<=" & std_ulogic'image(r2in.req.dc_req) & " r2req.alintr=" &
std_ulogic'image(r2in.req.align_intr) & " r2.busy<=" & std_ulogic'image(r2in.busy);
report "complete=" & std_ulogic'image(complete) & " intr=" & std_ulogic'image(r3.interrupt);
end if;
stage1_dreq <= stage1_dcreq;
if d_in.valid = '1' then
Expand Down Expand Up @@ -437,12 +478,15 @@ begin
v.virt_mode := l_in.virt_mode;
v.priv_mode := l_in.priv_mode;
v.ric := l_in.insn(19 downto 18);
if sprn(1) = '1' then
if sprn(8 downto 7) = "01" then
-- debug registers DAWR[X][01]
v.sprsel := '1' & sprn(3) & sprn(0);
elsif sprn(1) = '1' then
-- DSISR and DAR
v.sprsel := '1' & sprn(0);
v.sprsel := "01" & sprn(0);
else
-- PID and PTCR
v.sprsel := '0' & sprn(8);
v.sprsel := "00" & sprn(8);
end if;

lsu_sum := std_ulogic_vector(unsigned(l_in.addr1) + unsigned(l_in.addr2));
Expand Down Expand Up @@ -547,7 +591,7 @@ begin
v.ea_valid := '0';
when OP_MTSPR =>
v.write_spr := '1';
v.mmu_op := not sprn(1);
v.mmu_op := not (sprn(1) or sprn(2));
v.ea_valid := '0';
when OP_FETCH_FAILED =>
-- send it to the MMU to do the radix walk
Expand Down Expand Up @@ -577,6 +621,10 @@ begin
variable req : request_t;
variable dcreq : std_ulogic;
variable issue : std_ulogic;
variable addr : std_ulogic_vector(63 downto 3);
variable dawr_match : std_ulogic;
variable addl : unsigned(64 downto 3);
variable addu : unsigned(64 downto 3);
begin
v := r1;
issue := '0';
Expand Down Expand Up @@ -621,6 +669,28 @@ begin
end if;
end if;

-- Test for DAWR0/1 matches
dawr_match := '0';
for j in 0 to 1 loop
addr := req.addr(63 downto 3);
if req.priv_mode = '1' and r3.dawrx(j)(7) = '1' then
-- HRAMMC=1 => trim top bit from address
addr(63) := '0';
end if;
addl := unsigned('0' & addr) - unsigned('0' & r3.dawr(j));
addu := unsigned('0' & r3.dawr_uplim(j)) - unsigned('0' & addr);
if addl(64) = '0' and addu(64) = '0' and
dawrx_match_enable(r3.dawrx(j), req.virt_mode, req.priv_mode, req.store) then
dawr_match := '1';
end if;
end loop;
if dawr_match = '1' and issue = '1' and r3.dawr_upd = '0' and
(req.touch or req.sync or req.flush) = '0' then
issue := '0';
req.dc_req := '0';
req.dawr_intr := '1';
end if;

if flush = '1' then
v.req.valid := '0';
v.req.dc_req := '0';
Expand Down Expand Up @@ -659,7 +729,7 @@ begin
variable byte_offset : unsigned(2 downto 0);
variable interrupt : std_ulogic;
variable dbg_spr_rd : std_ulogic;
variable sprsel : std_ulogic_vector(1 downto 0);
variable sprsel : std_ulogic_vector(2 downto 0);
variable sprval : std_ulogic_vector(63 downto 0);
begin
v := r2;
Expand All @@ -681,17 +751,24 @@ begin
if dbg_spr_rd = '0' then
sprsel := r1.req.sprsel;
else
sprsel := dbg_spr_addr;
sprsel := '0' & dbg_spr_addr;
end if;
if sprsel(1) = '1' then
if sprsel(0) = '0' then
case sprsel is
when "100" =>
sprval := r3.dawr(0) & "000";
when "101" =>
sprval := r3.dawr(1) & "000";
when "110" =>
sprval := 48x"0" & r3.dawrx(0);
when "111" =>
sprval := 48x"0" & r3.dawrx(1);
when "010" =>
sprval := x"00000000" & r3.dsisr;
else
when "011" =>
sprval := r3.dar;
end if;
else
sprval := m_in.sprval;
end if;
when others =>
sprval := m_in.sprval; -- MMU regs
end case;
if dbg_spr_req = '0' then
v.dbg_spr_ack := '0';
elsif dbg_spr_rd = '1' and r2.dbg_spr_ack = '0' then
Expand All @@ -707,7 +784,7 @@ begin
v.wait_dc := r1.req.valid and r1.req.dc_req and not r1.req.load_sp and
not r1.req.incomplete;
v.wait_mmu := r1.req.valid and r1.req.mmu_op;
if r1.req.valid = '1' and r1.req.align_intr = '1' then
if r1.req.valid = '1' and (r1.req.align_intr = '1' or r1.req.dawr_intr = '1') then
v.busy := '1';
v.one_cycle := '0';
else
Expand Down Expand Up @@ -750,7 +827,7 @@ begin
v.busy := '0';
end if;

interrupt := (r2.req.valid and r2.req.align_intr) or
interrupt := (r2.req.valid and (r2.req.align_intr or r2.req.dawr_intr)) or
(d_in.error and (d_in.cache_paradox or d_in.reserve_nc)) or
m_in.err;
if interrupt = '1' then
Expand Down Expand Up @@ -808,6 +885,15 @@ begin
v.srr1 := (others => '0');
v.events := (others => '0');

-- Evaluate DAWR upper limits after a clock edge
v.dawr_upd := '0';
if r3.dawr_upd = '1' then
for i in 0 to num_dawr - 1 loop
v.dawr_uplim(i) := std_ulogic_vector(unsigned(r3.dawr(i)) +
unsigned(r3.dawrx(i)(15 downto 10)));
end loop;
end if;

-- load data formatting
-- shift and byte-reverse data bytes
for i in 0 to 7 loop
Expand Down Expand Up @@ -881,18 +967,35 @@ begin
-- generate alignment interrupt
exception := '1';
end if;
if r2.req.dawr_intr = '1' then
-- generate data storage interrupt
exception := '1';
end if;
if r2.req.do_update = '1' then
do_update := '1';
end if;
if r2.req.load_sp = '1' and r2.req.dc_req = '0' then
write_enable := '1';
end if;
if r2.req.write_spr = '1' and r2.req.mmu_op = '0' then
if r2.req.sprsel(0) = '0' then
v.dsisr := r2.req.store_data(31 downto 0);
else
v.dar := r2.req.store_data;
if r2.req.write_spr = '1' then
if r2.req.sprsel(2) = '1' then
v.dawr_upd := '1';
end if;
case r2.req.sprsel is
when "100" =>
v.dawr(0) := r2.req.store_data(63 downto 3);
when "101" =>
v.dawr(1) := r2.req.store_data(63 downto 3);
when "110" =>
v.dawrx(0) := r2.req.store_data(15 downto 0);
when "111" =>
v.dawrx(1) := r2.req.store_data(15 downto 0);
when "010" =>
v.dsisr := r2.req.store_data(31 downto 0);
when "011" =>
v.dar := r2.req.store_data;
when others =>
end case;
end if;
end if;

Expand Down Expand Up @@ -969,7 +1072,9 @@ begin
elsif r2.req.instr_fault = '0' then
v.srr1(47 - 34) := r2.req.prefixed;
v.dar := r2.req.addr;
if m_in.segerr = '0' then
if r2.req.dawr_intr = '1' or m_in.segerr = '0' then
dsisr(63 - 38) := not r2.req.load;
dsisr(63 - 41) := r2.req.dawr_intr;
v.intr_vec := 16#300#;
v.dsisr := dsisr;
else
Expand Down

0 comments on commit e4f5fba

Please sign in to comment.