diff --git a/common.vhdl b/common.vhdl index 41969e2..91f9ebd 100644 --- a/common.vhdl +++ b/common.vhdl @@ -249,6 +249,7 @@ package common is -- LPCR bit numbers constant LPCR_HAIL : integer := 63 - 37; constant LPCR_UPRT : integer := 63 - 41; + constant LPCR_EVIRT : integer := 63 - 42; constant LPCR_HR : integer := 63 - 43; constant LPCR_LD : integer := 63 - 46; constant LPCR_HEIC : integer := 63 - 59; @@ -322,6 +323,7 @@ package common is hdexcr_hyp: aspect_bits_t; hdexcr_enf: aspect_bits_t; lpcr_hail: std_ulogic; + lpcr_evirt: std_ulogic; lpcr_ld: std_ulogic; lpcr_heic: std_ulogic; lpcr_lpes: std_ulogic; @@ -333,7 +335,7 @@ 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_hail => '0', lpcr_evirt => '0', lpcr_ld => '1', lpcr_heic => '0', lpcr_lpes => '0', lpcr_hvice => '0', others => (others => '0')); @@ -420,9 +422,11 @@ package common is type bypass_data_t is record tag : instr_tag_t; + reg : gspr_index_t; data : std_ulogic_vector(63 downto 0); end record; - constant bypass_data_init : bypass_data_t := (tag => instr_tag_init, data => (others => '0')); + constant bypass_data_init : bypass_data_t := + (tag => instr_tag_init, reg => (others => '0'), data => (others => '0')); type cr_bypass_data_t is record tag : instr_tag_t; diff --git a/control.vhdl b/control.vhdl index b75fcc1..a760377 100644 --- a/control.vhdl +++ b/control.vhdl @@ -32,9 +32,10 @@ entity control is gpr_c_read_valid_in : in std_ulogic; gpr_c_read_in : in gspr_index_t; - execute_next_tag : in instr_tag_t; - execute_next_cr_tag : in instr_tag_t; - execute2_next_tag : in instr_tag_t; + execute_next_bypass : in bypass_data_t; + execute2_next_bypass : in bypass_data_t; + writeback_bypass : in bypass_data_t; + execute_next_cr_tag : in instr_tag_t; execute2_next_cr_tag : in instr_tag_t; cr_read_in : in std_ulogic; @@ -166,56 +167,78 @@ begin begin tag_a := instr_tag_init; for i in tag_number_t loop - if tag_regs(i).wr_gpr = '1' and tag_regs(i).recent = '1' and tag_regs(i).reg = gpr_a_read_in then + if tag_regs(i).valid = '1' and tag_regs(i).recent = '1' and + tag_regs(i).reg = gpr_a_read_in and gpr_a_read_valid_in = '1' then tag_a.valid := '1'; tag_a.tag := i; + if (EX1_BYPASS and tag_match(execute_next_bypass.tag, tag_a)) or + (EX1_BYPASS and tag_match(execute2_next_bypass.tag, tag_a)) or + tag_match(complete_in, tag_a) then + tag_a.valid := '0'; + end if; end if; end loop; - tag_b := instr_tag_init; - for i in tag_number_t loop - if tag_regs(i).wr_gpr = '1' and tag_regs(i).recent = '1' and tag_regs(i).reg = gpr_b_read_in then - tag_b.valid := '1'; - tag_b.tag := i; - end if; - end loop; - tag_c := instr_tag_init; - for i in tag_number_t loop - if tag_regs(i).wr_gpr = '1' and tag_regs(i).recent = '1' and tag_regs(i).reg = gpr_c_read_in then - tag_c.valid := '1'; - tag_c.tag := i; - end if; - end loop; - byp_a := "0000"; - if EX1_BYPASS and tag_match(execute_next_tag, tag_a) then + if EX1_BYPASS and execute_next_bypass.tag.valid = '1' and + execute_next_bypass.reg = gpr_a_read_in then byp_a(1) := '1'; - end if; - if EX1_BYPASS and tag_match(execute2_next_tag, tag_a) then + elsif EX1_BYPASS and execute2_next_bypass.tag.valid = '1' and + execute2_next_bypass.reg = gpr_a_read_in then byp_a(2) := '1'; - end if; - if tag_match(complete_in, tag_a) then + elsif writeback_bypass.tag.valid = '1' and + writeback_bypass.reg = gpr_a_read_in then byp_a(3) := '1'; end if; byp_a(0) := gpr_a_read_valid_in and (byp_a(1) or byp_a(2) or byp_a(3)); + + tag_b := instr_tag_init; + for i in tag_number_t loop + if tag_regs(i).valid = '1' and tag_regs(i).recent = '1' and + tag_regs(i).reg = gpr_b_read_in and gpr_b_read_valid_in = '1' then + tag_b.valid := '1'; + tag_b.tag := i; + if (EX1_BYPASS and tag_match(execute_next_bypass.tag, tag_b)) or + (EX1_BYPASS and tag_match(execute2_next_bypass.tag, tag_b)) or + tag_match(complete_in, tag_b) then + tag_b.valid := '0'; + end if; + end if; + end loop; byp_b := "0000"; - if EX1_BYPASS and tag_match(execute_next_tag, tag_b) then + if EX1_BYPASS and execute_next_bypass.tag.valid = '1' and + execute_next_bypass.reg = gpr_b_read_in then byp_b(1) := '1'; - end if; - if EX1_BYPASS and tag_match(execute2_next_tag, tag_b) then + elsif EX1_BYPASS and execute2_next_bypass.tag.valid = '1' and + execute2_next_bypass.reg = gpr_b_read_in then byp_b(2) := '1'; - end if; - if tag_match(complete_in, tag_b) then + elsif writeback_bypass.tag.valid = '1' and + writeback_bypass.reg = gpr_b_read_in then byp_b(3) := '1'; end if; byp_b(0) := gpr_b_read_valid_in and (byp_b(1) or byp_b(2) or byp_b(3)); + + tag_c := instr_tag_init; + for i in tag_number_t loop + if tag_regs(i).valid = '1' and tag_regs(i).recent = '1' and + tag_regs(i).reg = gpr_c_read_in and gpr_c_read_valid_in = '1' then + tag_c.valid := '1'; + tag_c.tag := i; + if (EX1_BYPASS and tag_match(execute_next_bypass.tag, tag_c)) or + (EX1_BYPASS and tag_match(execute2_next_bypass.tag, tag_c)) or + tag_match(complete_in, tag_c) then + tag_c.valid := '0'; + end if; + end if; + end loop; byp_c := "0000"; - if EX1_BYPASS and tag_match(execute_next_tag, tag_c) then + if EX1_BYPASS and execute_next_bypass.tag.valid = '1' and + execute_next_bypass.reg = gpr_c_read_in then byp_c(1) := '1'; - end if; - if EX1_BYPASS and tag_match(execute2_next_tag, tag_c) then + elsif EX1_BYPASS and execute2_next_bypass.tag.valid = '1' and + execute2_next_bypass.reg = gpr_c_read_in then byp_c(2) := '1'; - end if; - if tag_match(complete_in, tag_c) then + elsif writeback_bypass.tag.valid = '1' and + writeback_bypass.reg = gpr_c_read_in then byp_c(3) := '1'; end if; byp_c(0) := gpr_c_read_valid_in and (byp_c(1) or byp_c(2) or byp_c(3)); @@ -224,9 +247,7 @@ begin gpr_bypass_b <= byp_b; gpr_bypass_c <= byp_c; - gpr_tag_stall <= (tag_a.valid and gpr_a_read_valid_in and not byp_a(0)) or - (tag_b.valid and gpr_b_read_valid_in and not byp_b(0)) or - (tag_c.valid and gpr_c_read_valid_in and not byp_c(0)); + gpr_tag_stall <= tag_a.valid or tag_b.valid or tag_c.valid; incr_tag := curr_tag; instr_tag.tag <= curr_tag; diff --git a/decode2.vhdl b/decode2.vhdl index 0290840..b292080 100644 --- a/decode2.vhdl +++ b/decode2.vhdl @@ -279,10 +279,11 @@ begin gpr_c_read_valid_in => gpr_c_read_valid, gpr_c_read_in => gpr_c_read, - execute_next_tag => execute_bypass.tag, + execute_next_bypass => execute_bypass, execute_next_cr_tag => execute_cr_bypass.tag, - execute2_next_tag => execute2_bypass.tag, + execute2_next_bypass => execute2_bypass, execute2_next_cr_tag => execute2_cr_bypass.tag, + writeback_bypass => writeback_bypass, cr_read_in => cr_read_valid, cr_write_in => cr_write_valid, diff --git a/execute1.vhdl b/execute1.vhdl index 34fd03a..43831f3 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -106,7 +106,6 @@ architecture behaviour of execute1 is scv_trap : std_ulogic; write_tbl : std_ulogic; write_tbu : std_ulogic; - noop_spr_read : std_ulogic; send_hmsg : std_ulogic_vector(NCPUS-1 downto 0); clr_hmsg : std_ulogic; end record; @@ -426,6 +425,7 @@ architecture behaviour of execute1 is begin ret := (others => '0'); ret(LPCR_HAIL) := c.lpcr_hail; + ret(LPCR_EVIRT) := c.lpcr_evirt; ret(LPCR_UPRT) := '1'; ret(LPCR_HR) := '1'; ret(LPCR_LD) := c.lpcr_ld; @@ -1216,6 +1216,7 @@ begin variable owait : std_ulogic; variable srr1 : std_ulogic_vector(63 downto 0); variable c32, c64 : std_ulogic; + variable sprnum : spr_num_t; begin v := actions_type_init; v.e.write_data := alu_result; @@ -1224,7 +1225,7 @@ begin v.e.rc := e_in.rc; v.e.write_cr_data := write_cr_data; v.e.write_cr_mask := write_cr_mask; - v.e.write_cr_enable := e_in.output_cr; + v.e.write_cr_enable := e_in.output_cr or e_in.rc; v.e.write_xerc_enable := e_in.output_xer; v.e.xerc := xerc_in; v.new_msr := ex1.msr; @@ -1424,17 +1425,20 @@ begin when OP_DARN => when OP_MFMSR => when OP_MFSPR => + sprnum := decode_spr_num(e_in.insn); if e_in.spr_is_ram = '1' then if e_in.valid = '1' and not is_X(e_in.insn) then - report "MFSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) & + report "MFSPR to SPR " & integer'image(sprnum) & "=" & to_hstring(alu_result); end if; elsif e_in.spr_select.valid = '1' and e_in.spr_select.wonly = '0' then if e_in.valid = '1' and not is_X(e_in.insn) then - report "MFSPR to slow SPR " & integer'image(decode_spr_num(e_in.insn)); + report "MFSPR to slow SPR " & integer'image(sprnum); end if; slow_op := '1'; - v.se.noop_spr_read := e_in.spr_select.noop; + if e_in.spr_select.noop = '1' then + v.e.write_enable := '0'; + end if; if e_in.spr_select.ispmu = '0' then case e_in.spr_select.sel is when SPRSEL_LOGR => @@ -1449,14 +1453,15 @@ begin end if; else -- mfspr from unimplemented SPRs should be a nop in - -- supervisor mode and a program interrupt for user mode + -- supervisor mode and a program or HEAI interrupt for user mode + -- LPCR[EVIRT] = 1 makes it HEAI in privileged mode if e_in.valid = '1' and not is_X(e_in.insn) then - report "MFSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) & - " invalid"; + report "MFSPR to SPR " & integer'image(sprnum) & " invalid"; end if; slow_op := '1'; - v.se.noop_spr_read := '1'; - if ex1.msr(MSR_PR) = '1' then + v.e.write_enable := '0'; + if ex1.msr(MSR_PR) = '1' or ctrl.lpcr_evirt = '1' or + sprnum = 0 or sprnum = 4 or sprnum = 5 or sprnum = 6 then illegal := '1'; end if; end if; @@ -1502,8 +1507,9 @@ begin end if; end if; when OP_MTSPR => + sprnum := decode_spr_num(e_in.insn); if e_in.valid = '1' and not is_X(e_in.insn) then - report "MTSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) & + report "MTSPR to SPR " & integer'image(sprnum) & "=" & to_hstring(c_in); end if; v.se.write_pmuspr := e_in.spr_select.ispmu; @@ -1543,8 +1549,10 @@ begin end if; if e_in.spr_select.valid = '0' and e_in.spr_is_ram = '0' then -- mtspr to unimplemented SPRs should be a nop in - -- supervisor mode and a program interrupt for user mode - if ex1.msr(MSR_PR) = '1' then + -- supervisor mode and a program interrupt or HEAI for user mode + -- LPCR[EVIRT] = 1 makes it HEAI in privileged mode + if ex1.msr(MSR_PR) = '1' or ctrl.lpcr_evirt = '1' or + sprnum = 0 or sprnum = 4 or sprnum = 5 or sprnum = 6 then illegal := '1'; end if; end if; @@ -1746,6 +1754,8 @@ begin if valid_in = '1' then v.prev_op := e_in.insn_type; v.prev_prefixed := e_in.prefixed; + v.se.set_heir := actions.se.set_heir; + v.se.write_ic := actions.se.write_ic; end if; -- Determine if there is any interrupt to be taken @@ -1935,8 +1945,9 @@ begin v.fp_exception_next := '0'; end if; - bypass_data.tag.valid <= e_in.write_reg_enable and bypass_valid; - bypass_data.tag.tag <= e_in.instr_tag.tag; + bypass_data.tag.valid <= v.e.write_enable and bypass_valid; + bypass_data.tag.tag <= v.e.instr_tag.tag; + bypass_data.reg <= v.e.write_reg; bypass_data.data <= alu_result; bypass_cr_data.tag.valid <= e_in.output_cr and bypass_valid; @@ -2047,16 +2058,22 @@ begin -- Next insn adder used in a couple of places next_nia <= std_ulogic_vector(unsigned(ex1.e.last_nia) + 4); - v := ex2; - if stage2_stall = '0' then - v.e := ex1.e; - v.se := ex1.se; - v.ext_interrupt := ex1.ext_interrupt; - v.taken_branch_event := ex1.taken_branch_event; - v.br_mispredict := ex1.br_mispredict; - if ex1.advance_nia = '1' then - v.e.last_nia := next_nia; - end if; + v.log_addr_spr := ex2.log_addr_spr; + + v.e := ex1.e; + v.se := ex1.se; + v.ext_interrupt := ex1.ext_interrupt and not stage2_stall; + v.taken_branch_event := ex1.taken_branch_event and not stage2_stall; + v.br_mispredict := ex1.br_mispredict and not stage2_stall; + if stage2_stall = '1' then + v.e.last_nia := ex2.e.last_nia; + elsif ex1.advance_nia = '1' then + v.e.last_nia := next_nia; + end if; + if stage2_stall = '1' then + v.e.valid := '0'; + v.e.interrupt := '0'; + v.se := side_effect_init; end if; if ex1.se.mult_32s = '1' and ex1.oe = '1' then @@ -2110,9 +2127,7 @@ begin else rcresult := countbits_result; end if; - if ex1.se.noop_spr_read = '1' then - sprres := ex1.spr_write_data; - elsif ex1.res2_sel(0) = '0' then + if ex1.res2_sel(0) = '0' then sprres := spr_result; else sprres := pmu_to_x.spr_val; @@ -2144,14 +2159,11 @@ begin cr_mask(7) := '1'; end if; - if stage2_stall = '0' then - v.e.write_data := ex_result; - v.e.write_cr_data := cr_res; - v.e.write_cr_mask := cr_mask; - if ex1.e.rc = '1' and ex1.e.write_enable = '1' and v.e.valid = '1' then - v.e.write_cr_enable := '1'; - end if; + v.e.write_data := ex_result; + v.e.write_cr_data := cr_res; + v.e.write_cr_mask := cr_mask; + if stage2_stall = '0' then if ex1.se.write_msr = '1' then ctrl_tmp.msr <= ex1.msr; end if; @@ -2183,6 +2195,7 @@ begin end if; if ex1.se.write_lpcr = '1' then ctrl_tmp.lpcr_hail <= ex1.spr_write_data(LPCR_HAIL); + ctrl_tmp.lpcr_evirt <= ex1.spr_write_data(LPCR_EVIRT); ctrl_tmp.lpcr_ld <= ex1.spr_write_data(LPCR_LD); ctrl_tmp.lpcr_heic <= ex1.spr_write_data(LPCR_HEIC); ctrl_tmp.lpcr_lpes <= ex1.spr_write_data(LPCR_LPES); @@ -2240,13 +2253,15 @@ begin end if; end if; - bypass_valid := ex1.e.valid; - if stage2_stall = '1' and ex1.res2_sel(1) = '1' then - bypass_valid := '0'; - end if; + -- Don't bypass the result from mfspr to slow SPRs or PMU SPRs, + -- because we don't want to send the value while stalled because it + -- might change, and we don't want bypass_valid to depend on + -- stage2_stall for timing reasons. + bypass_valid := ex1.e.valid and not ex1.res2_sel(1); bypass2_data.tag.valid <= ex1.e.write_enable and bypass_valid; bypass2_data.tag.tag <= ex1.e.instr_tag.tag; + bypass2_data.reg <= ex1.e.write_reg; bypass2_data.data <= ex_result; bypass2_cr_data.tag.valid <= (ex1.e.write_cr_enable or (ex1.e.rc and ex1.e.write_enable)) diff --git a/tests/fpu/fpu.c b/tests/fpu/fpu.c index c13110f..5e45038 100644 --- a/tests/fpu/fpu.c +++ b/tests/fpu/fpu.c @@ -1795,6 +1795,49 @@ int fpu_test_26(void) return trapit(0, test26); } +/* Check for enabled invalid exception suppressing write of result */ +int test27(long arg) +{ + unsigned long operands[4]; + unsigned long result; + + operands[0] = 0xabcd1234ef895670; + operands[1] = 0xbff0000000000000; + operands[2] = 0xef895670abcd1234; + operands[3] = 0; + set_fpscr(FPS_VE); + asm("lfd 3,0(%0); isync; lfd 4,8(%0); lfd 3,16(%0); fsqrt 3,4; stfd 3,0(%1)" + : : "b" (&operands), "b" (&result) : "memory"); + if (result != 0xef895670abcd1234) { + if (result == 0x7ffc000000000000) + return 1; + if (result == 0xabcd1234ef895670) + return 2; + print_hex(result, 16, " "); + return 3; + } + + set_fpscr(FPS_ZE); + asm("lfd 3,0(%0); isync; lfd 4,8(%0); lfd 5,24(%0); lfd 3,16(%0); fdiv 3,4,5; stfd 3,0(%1)" + : : "b" (&operands), "b" (&result) : "memory"); + if (result != 0xef895670abcd1234) { + if (result == 0x7ffc000000000000) + return 4; + if (result == 0xabcd1234ef895670) + return 5; + print_hex(result, 16, " "); + return 6; + } + + return 0; +} + +int fpu_test_27(void) +{ + enable_fp(); + return trapit(0, test27); +} + int fail = 0; void do_test(int num, int (*test)(void)) @@ -1846,6 +1889,7 @@ int main(void) do_test(24, fpu_test_24); do_test(25, fpu_test_25); do_test(26, fpu_test_26); + do_test(27, fpu_test_27); return fail; } diff --git a/tests/modes/modes.c b/tests/modes/modes.c index f37e70b..b083671 100644 --- a/tests/modes/modes.c +++ b/tests/modes/modes.c @@ -7,6 +7,8 @@ #define MSR_LE 0x1 #define MSR_DR 0x10 #define MSR_IR 0x20 +#define MSR_PR 0x4000 +#define MSR_EE 0x8000 #define MSR_SF 0x8000000000000000ul extern unsigned long callit(unsigned long arg1, unsigned long arg2, @@ -27,6 +29,7 @@ static inline void do_tlbie(unsigned long rb, unsigned long rs) #define DSISR 18 #define DAR 19 +#define DEC 22 #define SRR0 26 #define SRR1 27 #define PID 48 @@ -35,6 +38,7 @@ static inline void do_tlbie(unsigned long rb, unsigned long rs) #define SPRG3 275 #define HSRR0 314 #define HSRR1 315 +#define HEIR 339 #define PTCR 464 static inline unsigned long mfspr(int sprnum) @@ -464,6 +468,39 @@ int mode_test_8(void) return 0; } +int test_9_mf(void) +{ + mfspr(2); + return 0; +} + +int test_9_mt(unsigned long arg) +{ + mtspr(2, arg); + return 0; +} + +int mode_test_9(void) +{ + unsigned long ret, msr; + + /* + * Test that mfspr/mtspr to unimplemented SPRs in user mode + * causes an HEAI and sets HEIR. + */ + msr = MSR_SF | MSR_LE | MSR_PR | MSR_EE | MSR_IR | MSR_DR; + mtspr(DEC, 0x7fffffff); + ret = callit(0, 0, (unsigned long) test_9_mf, msr); + if (ret != 0xe40 || + (mfspr(HEIR) & 0xfc1fffff) != ((31ul << 26) | (2 << 16) | (339 << 1))) + return 1; + ret = callit(0, 0, (unsigned long) test_9_mt, msr); + if (ret != 0xe40 || + (mfspr(HEIR) & 0xfc1fffff) != ((31ul << 26) | (2 << 16) | (467 << 1))) + return 2; + return 0; +} + int fail = 0; void do_test(int num, int (*test)(void)) @@ -510,6 +547,7 @@ int main(void) do_test(6, mode_test_6); do_test(7, mode_test_7); do_test(8, mode_test_8); + do_test(9, mode_test_9); return fail; } diff --git a/tests/spr_read/head.S b/tests/spr_read/head.S index 92d69bb..1c84b4f 100644 --- a/tests/spr_read/head.S +++ b/tests/spr_read/head.S @@ -44,3 +44,91 @@ boot_entry: bctrl attn // terminate on exit b . + + .globl read_sprn +read_sprn: + nop + nop + mr %r0,%r3 + mr %r3,%r4 + cmpdi %r0,0 + beq 0f + cmpdi %r0,1 + beq 1f + cmpdi %r0,4 + beq 4f + cmpdi %r0,5 + beq 5f + cmpdi %r0,6 + beq 6f + mfspr %r3,179 + blr +0: mfspr %r3,0 + blr +1: mfspr %r3,1 + blr +4: mfspr %r3,4 + blr +5: mfspr %r3,5 + blr +6: mfspr %r3,6 + blr + + .globl write_sprn +write_sprn: + nop + nop + mr %r0,%r3 + li %r3,0 + cmpdi %r0,0 + beq 0f + cmpdi %r0,1 + beq 1f + cmpdi %r0,4 + beq 4f + cmpdi %r0,5 + beq 5f + cmpdi %r0,6 + beq 6f + mtspr 179,%r3 + blr +0: mtspr 0,%r3 + blr +1: mtspr 1,%r3 + blr +4: mtspr 4,%r3 + blr +5: mtspr 5,%r3 + blr +6: mtspr 6,%r3 + blr + +#define EXCEPTION(nr) \ + .= nr ;\ + li %r3,nr ;\ + blr + + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + EXCEPTION(0x500) + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + EXCEPTION(0x900) + EXCEPTION(0x980) + EXCEPTION(0xa00) + EXCEPTION(0xb00) + EXCEPTION(0xc00) + EXCEPTION(0xd00) + EXCEPTION(0xe00) + EXCEPTION(0xe20) + EXCEPTION(0xe40) + EXCEPTION(0xe60) + EXCEPTION(0xe80) + EXCEPTION(0xf00) + EXCEPTION(0xf20) + EXCEPTION(0xf40) + EXCEPTION(0xf60) + EXCEPTION(0xf80) diff --git a/tests/spr_read/spr_read.c b/tests/spr_read/spr_read.c index 6d2859e..2bf183a 100644 --- a/tests/spr_read/spr_read.c +++ b/tests/spr_read/spr_read.c @@ -8,6 +8,9 @@ #define PASS "PASS\n" #define FAIL "FAIL\n" +extern long read_sprn(long, long); +extern long write_sprn(long); + // i < 100 void print_test(char *str) { @@ -44,9 +47,24 @@ void print_test(char *str) #define __stringify_1(x...) #x #define __stringify(x...) __stringify_1(x) +void print_hex(unsigned long val, int ndigits, const char *str) +{ + int i, x; + + for (i = (ndigits - 1) * 4; i >= 0; i -= 4) { + x = (val >> i) & 0xf; + if (x >= 10) + putchar(x + 'a' - 10); + else + putchar(x + '0'); + } + puts(str); +} + int main(void) { - unsigned long tmp; + unsigned long tmp, r; + int fail = 0; console_init(); @@ -86,7 +104,47 @@ int main(void) DO_ONE(SPR_PTCR); DO_ONE(SPR_PVR); - puts(PASS); + /* + * Test no-op behaviour of reserved no-op SPRs, + * and of accesses to undefined SPRs in privileged mode. + */ + print_test("reserved no-op"); + __asm__ __volatile__("mtspr 811,%0" : : "r" (7838)); + __asm__ __volatile__("li %0,%1; mfspr %0,811" : "=r" (tmp) : "i" (2398)); + if (tmp == 2398) { + puts(PASS); + } else { + puts(FAIL); + fail = 1; + } + + print_test("undefined SPR"); + r = write_sprn(179); + tmp = read_sprn(179, 2498); + if (r == 0 && tmp == 2498) { + puts(PASS); + } else { + puts(FAIL); + fail = 1; + } + + print_test("read SPR 0/4/5/6"); + if (read_sprn(0, 1234) == 0xe40 && read_sprn(2, 1234) == 1234 && + read_sprn(4, 1234) == 0xe40 && read_sprn(5, 1234) == 0xe40 && + read_sprn(6, 1234) == 0xe40 && + write_sprn(0) == 0xe40 && write_sprn(2) == 0 && + write_sprn(4) == 0xe40 && write_sprn(5) == 0xe40 && + write_sprn(6) == 0xe40) { + puts(PASS); + } else { + puts(FAIL); + fail = 1; + } + + if (!fail) + puts(PASS); + else + puts(FAIL); - return 0; + return fail; } diff --git a/tests/test_fpu.bin b/tests/test_fpu.bin index 24878af..b2a293c 100755 Binary files a/tests/test_fpu.bin and b/tests/test_fpu.bin differ diff --git a/tests/test_fpu.console_out b/tests/test_fpu.console_out index 987a633..6913d7e 100644 --- a/tests/test_fpu.console_out +++ b/tests/test_fpu.console_out @@ -24,3 +24,4 @@ test 23:PASS test 24:PASS test 25:PASS test 26:PASS +test 27:PASS diff --git a/tests/test_modes.bin b/tests/test_modes.bin index d0c24d7..0649686 100755 Binary files a/tests/test_modes.bin and b/tests/test_modes.bin differ diff --git a/tests/test_modes.console_out b/tests/test_modes.console_out index 25e791c..2fe36d2 100644 --- a/tests/test_modes.console_out +++ b/tests/test_modes.console_out @@ -6,3 +6,4 @@ test 05:PASS test 06:PASS test 07:PASS test 08:PASS +test 09:PASS diff --git a/tests/test_spr_read.bin b/tests/test_spr_read.bin index 68f6cd8..96a95bc 100755 Binary files a/tests/test_spr_read.bin and b/tests/test_spr_read.bin differ diff --git a/tests/test_spr_read.console_out b/tests/test_spr_read.console_out index 8a032d4..9722dfb 100644 --- a/tests/test_spr_read.console_out +++ b/tests/test_spr_read.console_out @@ -22,4 +22,7 @@ Test SPR_HSPRG1:PASS Test SPR_PID:PASS Test SPR_PTCR:PASS Test SPR_PVR:PASS +Test reserved no-op:PASS +Test undefined SPR:PASS +Test read SPR 0/4/5/6:PASS PASS diff --git a/writeback.vhdl b/writeback.vhdl index 49a53cc..944bae5 100644 --- a/writeback.vhdl +++ b/writeback.vhdl @@ -203,6 +203,7 @@ begin -- Register write data bypass to decode2 wb_bypass.tag.tag <= complete_out.tag; wb_bypass.tag.valid <= complete_out.valid and w_out.write_enable; + wb_bypass.reg <= w_out.write_reg; wb_bypass.data <= w_out.write_data; end process;