diff --git a/.gitignore b/.gitignore index 3c7c0b5..776be9b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ *.cf *.s *_tb -main_ram.bin \ No newline at end of file +main_ram.bin +tests/*/*.bin +tests/*/*.hex +tests/*/*.elf diff --git a/Makefile b/Makefile index a1658d4..2f90f22 100644 --- a/Makefile +++ b/Makefile @@ -128,14 +128,18 @@ dmi_dtm_tb: dmi_dtm_tb.o sim_vhpi_c.o sim_bram_helpers_c.o $(GHDL) -e $(GHDLFLAGS) -Wl,sim_vhpi_c.o -Wl,sim_bram_helpers_c.o $@ tests = $(sort $(patsubst tests/%.out,%,$(wildcard tests/*.out))) +tests_console = $(sort $(patsubst tests/%.console_out,%,$(wildcard tests/*.console_out))) -check: $(tests) test_micropython test_micropython_long +check: $(tests) $(test_console) test_micropython test_micropython_long -check_light: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 test_micropython test_micropython_long +check_light: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 test_micropython test_micropython_long $(tests_console) $(tests): core_tb @./scripts/run_test.sh $@ +$(tests_console): core_tb + @./scripts/run_test_console.sh $@ + test_micropython: core_tb @./scripts/test_micropython.py diff --git a/common.vhdl b/common.vhdl index 1ed0606..9e4ef99 100644 --- a/common.vhdl +++ b/common.vhdl @@ -16,6 +16,7 @@ package common is constant SPR_LR : spr_num_t := 8; constant SPR_CTR : spr_num_t := 9; constant SPR_TB : spr_num_t := 268; + constant SPR_DEC : spr_num_t := 22; constant SPR_SRR0 : spr_num_t := 26; constant SPR_SRR1 : spr_num_t := 27; constant SPR_HSRR0 : spr_num_t := 314; @@ -62,9 +63,16 @@ package common is end record; constant xerc_init : xer_common_t := (others => '0'); + type irq_state_t is (WRITE_SRR0, WRITE_SRR1); + -- This needs to die... type ctrl_t is record tb: std_ulogic_vector(63 downto 0); + dec: std_ulogic_vector(63 downto 0); + msr: std_ulogic_vector(63 downto 0); + irq_state : irq_state_t; + irq_nia: std_ulogic_vector(63 downto 0); + srr1: std_ulogic_vector(63 downto 0); end record; type Fetch1ToIcacheType is record diff --git a/decode1.vhdl b/decode1.vhdl index e9dae1e..5230fa4 100644 --- a/decode1.vhdl +++ b/decode1.vhdl @@ -94,6 +94,7 @@ architecture behaviour of decode1 is 2#0011000001# => '1', -- crxor 2#0010010110# => '1', -- isync 2#0000000000# => '1', -- mcrf + 2#0000010010# => '1', -- rfid others => '0' ); @@ -109,6 +110,8 @@ architecture behaviour of decode1 is 2#100# => (ALU, OP_BCREG, SPR, SPR, NONE, SPR, '1', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '1', '0'), -- isync 2#111# => (ALU, OP_ISYNC, NONE, NONE, NONE, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '1'), + -- rfid + 2#101# => (ALU, OP_RFID, SPR, SPR, NONE, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'), others => illegal_inst ); @@ -241,12 +244,14 @@ architecture behaviour of decode1 is -- 2#1000000000# mcrxr -- 2#1001000000# mcrxrx 2#0000010011# => (ALU, OP_MFCR, NONE, NONE, NONE, RT, '1', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'), -- mfcr/mfocrf + 2#0001010011# => (ALU, OP_MFMSR, NONE, NONE, NONE, RT, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '1'), -- mfmsr 2#0101010011# => (ALU, OP_MFSPR, SPR, NONE, NONE, RT, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'), -- mfspr 2#0100001001# => (ALU, OP_MOD, RA, RB, NONE, RT, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'), -- modud 2#0100001011# => (ALU, OP_MOD, RA, RB, NONE, RT, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '1', '0', NONE, '0', '0'), -- moduw 2#1100001001# => (ALU, OP_MOD, RA, RB, NONE, RT, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '1', NONE, '0', '0'), -- modsd 2#1100001011# => (ALU, OP_MOD, RA, RB, NONE, RT, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '1', '1', NONE, '0', '0'), -- modsw 2#0010010000# => (ALU, OP_MTCRF, NONE, NONE, RS, NONE, '0', '1', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'), -- mtcrf/mtocrf + 2#0010110010# => (ALU, OP_MTMSRD, NONE, NONE, RS, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '1'), -- mtmsrd # ignore top bits and d 2#0111010011# => (ALU, OP_MTSPR, NONE, NONE, RS, SPR, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'), -- mtspr 2#0001001001# => (ALU, OP_MUL_H64, RA, RB, NONE, RT, '0', '1', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '1', RC, '0', '0'), -- mulhd 2#0000001001# => (ALU, OP_MUL_H64, RA, RB, NONE, RT, '0', '1', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', RC, '0', '0'), -- mulhdu @@ -337,8 +342,9 @@ architecture behaviour of decode1 is -- unit internal in1 in2 in3 out CR CR inv inv cry cry ldst BR sgn upd rsrv 32b sgn rc lk sgl -- op in out A out in out len ext pipe - constant attn_instr : decode_rom_t := (ALU, OP_ILLEGAL, NONE, NONE, NONE, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '1'); + constant attn_instr : decode_rom_t := (ALU, OP_ATTN, NONE, NONE, NONE, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '1'); constant nop_instr : decode_rom_t := (ALU, OP_NOP, NONE, NONE, NONE, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'); + constant sc_instr : decode_rom_t := (ALU, OP_SC, NONE, NONE, NONE, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0'); constant sim_cfg_instr : decode_rom_t := (ALU, OP_SIM_CONFIG,NONE, NONE, NONE, RT, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '1'); begin @@ -397,6 +403,9 @@ begin elsif std_match(f_in.insn, "01100000000000000000000000000000") then report "PPC_nop"; v.decode := nop_instr; + elsif std_match(f_in.insn, "010001--------------0000000---1-") then + report "PPC_sc"; + v.decode := sc_instr; elsif std_match(f_in.insn, "000001---------------0000000011-") then report "PPC_SIM_CONFIG"; v.decode := sim_cfg_instr; @@ -433,6 +442,10 @@ begin if is_fast_spr(v.ispr1) = '0' then v.decode.sgl_pipe := '1'; end if; + elsif v.decode.insn_type = OP_RFID then + report "PPC RFID"; + v.ispr1 := fast_spr_num(SPR_SRR0); + v.ispr2 := fast_spr_num(SPR_SRR1); end if; if flush_in = '1' then diff --git a/decode_types.vhdl b/decode_types.vhdl index 21d8b68..b2d9846 100644 --- a/decode_types.vhdl +++ b/decode_types.vhdl @@ -11,16 +11,15 @@ package decode_types is OP_DCBZ, OP_DIV, OP_DIVE, OP_EXTS, OP_EXTSWSLI, OP_ICBI, OP_ICBT, OP_ISEL, OP_ISYNC, OP_LOAD, OP_STORE, OP_MADDHD, OP_MADDHDU, OP_MADDLD, OP_MCRF, - OP_MCRXR, OP_MCRXRX, OP_MFCR, OP_MFSPR, OP_MOD, - OP_MTCRF, OP_MTSPR, OP_MUL_L64, + OP_MCRXR, OP_MCRXRX, OP_MFCR, OP_MFMSR, OP_MFSPR, OP_MOD, + OP_MTCRF, OP_MTMSRD, OP_MTSPR, OP_MUL_L64, OP_MUL_H64, OP_MUL_H32, OP_OR, - OP_POPCNT, OP_PRTY, - OP_RLC, OP_RLCL, OP_RLCR, OP_SETB, + OP_POPCNT, OP_PRTY, OP_RFID, + OP_RLC, OP_RLCL, OP_RLCR, OP_SC, OP_SETB, OP_SHL, OP_SHR, OP_SYNC, OP_TD, OP_TDI, OP_TW, OP_TWI, OP_XOR, OP_SIM_CONFIG ); - type input_reg_a_t is (NONE, RA, RA_OR_ZERO, SPR); type input_reg_b_t is (NONE, RB, CONST_UI, CONST_SI, CONST_SI_HI, CONST_UI_HI, CONST_LI, CONST_BD, CONST_DS, CONST_M1, CONST_SH, CONST_SH32, SPR); type input_reg_c_t is (NONE, RS); diff --git a/execute1.vhdl b/execute1.vhdl index 4703049..d006002 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -53,9 +53,8 @@ architecture behaviour of execute1 is signal a_in, b_in, c_in : std_ulogic_vector(63 downto 0); - signal ctrl: ctrl_t := (others => (others => '0')); - signal ctrl_tmp: ctrl_t := (others => (others => '0')); - + signal ctrl: ctrl_t := (irq_state => WRITE_SRR0, others => (others => '0')); + signal ctrl_tmp: ctrl_t := (irq_state => WRITE_SRR0, others => (others => '0')); signal right_shift, rot_clear_left, rot_clear_right: std_ulogic; signal rotator_result: std_ulogic_vector(63 downto 0); signal rotator_carry: std_ulogic; @@ -112,6 +111,27 @@ architecture behaviour of execute1 is end case; end; + function msr_copy(msr: std_ulogic_vector(63 downto 0)) + return std_ulogic_vector is + variable msr_out: std_ulogic_vector(63 downto 0); + begin + -- ISA says this: + -- Defined MSR bits are classified as either full func- + -- tion or partial function. Full function MSR bits are + -- saved in SRR1 or HSRR1 when an interrupt other + -- than a System Call Vectored interrupt occurs and + -- restored by rfscv, rfid, or hrfid, while partial func- + -- tion MSR bits are not saved or restored. + -- Full function MSR bits lie in the range 0:32, 37:41, and + -- 48:63, and partial function MSR bits lie in the range + -- 33:36 and 42:47. + msr_out := (others => '0'); + msr_out(32 downto 0) := msr(32 downto 0); + msr_out(41 downto 37) := msr(41 downto 37); + msr_out(63 downto 48) := msr(63 downto 48); + return msr_out; + end; + begin rotator_0: entity work.rotator @@ -215,6 +235,8 @@ begin variable msb_a, msb_b : std_ulogic; variable a_lt : std_ulogic; variable lv : Execute1ToLoadstore1Type; + variable irq_valid : std_ulogic; + variable exception : std_ulogic; begin result := (others => '0'); result_with_carry := (others => '0'); @@ -341,6 +363,13 @@ begin ctrl_tmp <= ctrl; -- FIXME: run at 512MHz not core freq ctrl_tmp.tb <= std_ulogic_vector(unsigned(ctrl.tb) + 1); + ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1); + + irq_valid := '0'; + if ctrl.msr(63 - 48) = '1' and ctrl.dec(63) = '1' then + report "IRQ valid"; + irq_valid := '1'; + end if; terminate_out <= '0'; icache_inval <= '0'; @@ -355,7 +384,28 @@ begin rot_clear_left <= '1' when e_in.insn_type = OP_RLC or e_in.insn_type = OP_RLCL else '0'; rot_clear_right <= '1' when e_in.insn_type = OP_RLC or e_in.insn_type = OP_RLCR else '0'; - if e_in.valid = '1' then + ctrl_tmp.irq_state <= WRITE_SRR0; + exception := '0'; + + if ctrl.irq_state = WRITE_SRR1 then + v.e.write_reg := fast_spr_num(SPR_SRR1); + result := ctrl.srr1; + result_en := '1'; + ctrl_tmp.msr(63 - 48) <= '0'; -- clear EE + f_out.redirect <= '1'; + f_out.redirect_nia <= ctrl.irq_nia; + v.e.valid := '1'; + report "Writing SRR1: " & to_hstring(ctrl.srr1); + + elsif irq_valid = '1' then + -- we need two cycles to write srr0 and 1 + -- will need more when we have to write DSISR, DAR and HIER + exception := '1'; + ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#900#, 64)); + ctrl_tmp.srr1 <= msr_copy(ctrl.msr); + result := e_in.nia; + + elsif e_in.valid = '1' then v.e.valid := '1'; v.e.write_reg := e_in.write_reg; @@ -367,8 +417,28 @@ begin case_0: case e_in.insn_type is when OP_ILLEGAL => - terminate_out <= '1'; + -- we need two cycles to write srr0 and 1 + -- will need more when we have to write DSISR, DAR and HIER + exception := '1'; + ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#700#, 64)); + ctrl_tmp.srr1 <= msr_copy(ctrl.msr); + -- Since we aren't doing Hypervisor emulation assist (0xe40) we + -- set bit 44 to indicate we have an illegal + ctrl_tmp.srr1(63 - 44) <= '1'; + result := e_in.nia; report "illegal"; + when OP_SC => + -- FIXME Assume everything is SC (not SCV) for now + -- we need two cycles to write srr0 and 1 + -- will need more when we have to write DSISR, DAR and HIER + exception := '1'; + ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#C00#, 64)); + ctrl_tmp.srr1 <= msr_copy(ctrl.msr); + result := std_logic_vector(unsigned(e_in.nia) + 4); + report "sc"; + when OP_ATTN => + terminate_out <= '1'; + report "ATTN"; when OP_NOP => -- Do nothing when OP_ADD | OP_CMP => @@ -474,6 +544,11 @@ begin f_out.redirect <= '1'; f_out.redirect_nia <= b_in(63 downto 2) & "00"; end if; + + when OP_RFID => + f_out.redirect <= '1'; + f_out.redirect_nia <= a_in(63 downto 2) & "00"; -- srr0 + ctrl_tmp.msr <= msr_copy(std_ulogic_vector(signed(b_in))); -- srr1 when OP_CMPB => result := ppc_cmpb(c_in, b_in); result_en := '1'; @@ -546,7 +621,12 @@ begin end if; end loop; end if; + when OP_MFMSR => + result := msr_copy(ctrl.msr); + result_en := '1'; when OP_MFSPR => + report "MFSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) & + "=" & to_hstring(a_in); if is_fast_spr(e_in.read_reg1) then result := a_in; if decode_spr_num(e_in.insn) = SPR_XER then @@ -563,6 +643,8 @@ begin case decode_spr_num(e_in.insn) is when SPR_TB => result := ctrl.tb; + when SPR_DEC => + result := ctrl.dec; when others => result := (others => '0'); end case; @@ -596,6 +678,9 @@ begin v.e.write_cr_mask := num_to_fxm(crnum); end if; v.e.write_cr_data := c_in(31 downto 0); + when OP_MTMSRD => + -- FIXME handle just the bits we need to. + ctrl_tmp.msr <= msr_copy(c_in); when OP_MTSPR => report "MTSPR to SPR " & integer'image(decode_spr_num(e_in.insn)) & "=" & to_hstring(c_in); @@ -611,10 +696,12 @@ begin v.e.write_xerc_enable := '1'; end if; else --- TODO: Implement slow SPRs --- case decode_spr_num(e_in.insn) is --- when others => --- end case; + -- slow spr + case decode_spr_num(e_in.insn) is + when SPR_DEC => + ctrl_tmp.dec <= c_in; + when others => + end case; end if; when OP_POPCNT => result := popcnt_result; @@ -730,6 +817,16 @@ begin end if; end if; + if exception = '1' then + v.e.write_reg := fast_spr_num(SPR_SRR0); + if e_in.valid = '1' then + result_en := '1'; + ctrl_tmp.irq_state <= WRITE_SRR1; + stall_out <= '1'; + v.e.valid := '0'; + end if; + end if; + v.e.write_data := result; v.e.write_enable := result_en; diff --git a/hello_world/Makefile b/hello_world/Makefile index 89f3e00..0cbc69f 100644 --- a/hello_world/Makefile +++ b/hello_world/Makefile @@ -15,8 +15,8 @@ LDFLAGS = -T powerpc.lds all: hello_world.hex -hello_world.elf: hello_world.o head.o - $(LD) $(LDFLAGS) -o hello_world.elf hello_world.o head.o +hello_world.elf: hello_world.o head.o console.o + $(LD) $(LDFLAGS) -o hello_world.elf hello_world.o head.o console.o hello_world.bin: hello_world.elf $(OBJCOPY) -O binary hello_world.elf hello_world.bin diff --git a/hello_world/console.c b/hello_world/console.c new file mode 100644 index 0000000..640eb48 --- /dev/null +++ b/hello_world/console.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +/* + * Core UART functions to implement for a port + */ + +static uint64_t potato_uart_base; + +#define PROC_FREQ 50000000 +#define UART_FREQ 115200 +#define UART_BASE 0xc0002000 + +#define POTATO_CONSOLE_TX 0x00 +#define POTATO_CONSOLE_RX 0x08 +#define POTATO_CONSOLE_STATUS 0x10 +#define POTATO_CONSOLE_STATUS_RX_EMPTY 0x01 +#define POTATO_CONSOLE_STATUS_TX_EMPTY 0x02 +#define POTATO_CONSOLE_STATUS_RX_FULL 0x04 +#define POTATO_CONSOLE_STATUS_TX_FULL 0x08 +#define POTATO_CONSOLE_CLOCK_DIV 0x18 +#define POTATO_CONSOLE_IRQ_EN 0x20 + +static uint64_t potato_uart_reg_read(int offset) +{ + uint64_t addr; + uint64_t val; + + addr = potato_uart_base + offset; + + val = *(volatile uint64_t *)addr; + + return val; +} + +static void potato_uart_reg_write(int offset, uint64_t val) +{ + uint64_t addr; + + addr = potato_uart_base + offset; + + *(volatile uint64_t *)addr = val; +} + +static int potato_uart_rx_empty(void) +{ + uint64_t val; + + val = potato_uart_reg_read(POTATO_CONSOLE_STATUS); + + if (val & POTATO_CONSOLE_STATUS_RX_EMPTY) + return 1; + + return 0; +} + +static int potato_uart_tx_full(void) +{ + uint64_t val; + + val = potato_uart_reg_read(POTATO_CONSOLE_STATUS); + + if (val & POTATO_CONSOLE_STATUS_TX_FULL) + return 1; + + return 0; +} + +static char potato_uart_read(void) +{ + uint64_t val; + + val = potato_uart_reg_read(POTATO_CONSOLE_RX); + + return (char)(val & 0x000000ff); +} + +static void potato_uart_write(char c) +{ + uint64_t val; + + val = c; + + potato_uart_reg_write(POTATO_CONSOLE_TX, val); +} + +static unsigned long potato_uart_divisor(unsigned long proc_freq, unsigned long uart_freq) +{ + return proc_freq / (uart_freq * 16) - 1; +} + +void potato_uart_init(void) +{ + potato_uart_base = UART_BASE; + + potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(PROC_FREQ, UART_FREQ)); +} + +int getchar(void) +{ + while (potato_uart_rx_empty()) + /* Do nothing */ ; + + return potato_uart_read(); +} + +void putchar(unsigned char c) +{ + while (potato_uart_tx_full()) + /* Do Nothing */; + + potato_uart_write(c); +} + +void putstr(const char *str, unsigned long len) +{ + for (unsigned long i = 0; i < len; i++) { + putchar(str[i]); + } +} + +size_t strlen(const char *s) +{ + size_t len = 0; + + while (*s++) + len++; + + return len; +} diff --git a/hello_world/console.h b/hello_world/console.h new file mode 100644 index 0000000..85e7b36 --- /dev/null +++ b/hello_world/console.h @@ -0,0 +1,5 @@ +void potato_uart_init(void); +int getchar(void); +void putchar(unsigned char c); +void putstr(const char *str, unsigned long len); +size_t strlen(const char *s); diff --git a/hello_world/hello_world.c b/hello_world/hello_world.c index e4e9835..f1d1367 100644 --- a/hello_world/hello_world.c +++ b/hello_world/hello_world.c @@ -3,133 +3,7 @@ #include #include -/* - * Core UART functions to implement for a port - */ - -static uint64_t potato_uart_base; - -#define PROC_FREQ 50000000 -#define UART_FREQ 115200 -#define UART_BASE 0xc0002000 - -#define POTATO_CONSOLE_TX 0x00 -#define POTATO_CONSOLE_RX 0x08 -#define POTATO_CONSOLE_STATUS 0x10 -#define POTATO_CONSOLE_STATUS_RX_EMPTY 0x01 -#define POTATO_CONSOLE_STATUS_TX_EMPTY 0x02 -#define POTATO_CONSOLE_STATUS_RX_FULL 0x04 -#define POTATO_CONSOLE_STATUS_TX_FULL 0x08 -#define POTATO_CONSOLE_CLOCK_DIV 0x18 -#define POTATO_CONSOLE_IRQ_EN 0x20 - -static uint64_t potato_uart_reg_read(int offset) -{ - uint64_t addr; - uint64_t val; - - addr = potato_uart_base + offset; - - val = *(volatile uint64_t *)addr; - - return val; -} - -static void potato_uart_reg_write(int offset, uint64_t val) -{ - uint64_t addr; - - addr = potato_uart_base + offset; - - *(volatile uint64_t *)addr = val; -} - -static int potato_uart_rx_empty(void) -{ - uint64_t val; - - val = potato_uart_reg_read(POTATO_CONSOLE_STATUS); - - if (val & POTATO_CONSOLE_STATUS_RX_EMPTY) - return 1; - - return 0; -} - -static int potato_uart_tx_full(void) -{ - uint64_t val; - - val = potato_uart_reg_read(POTATO_CONSOLE_STATUS); - - if (val & POTATO_CONSOLE_STATUS_TX_FULL) - return 1; - - return 0; -} - -static char potato_uart_read(void) -{ - uint64_t val; - - val = potato_uart_reg_read(POTATO_CONSOLE_RX); - - return (char)(val & 0x000000ff); -} - -static void potato_uart_write(char c) -{ - uint64_t val; - - val = c; - - potato_uart_reg_write(POTATO_CONSOLE_TX, val); -} - -static unsigned long potato_uart_divisor(unsigned long proc_freq, unsigned long uart_freq) -{ - return proc_freq / (uart_freq * 16) - 1; -} - -void potato_uart_init(void) -{ - potato_uart_base = UART_BASE; - - potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(PROC_FREQ, UART_FREQ)); -} - -int getchar(void) -{ - while (potato_uart_rx_empty()) - /* Do nothing */ ; - - return potato_uart_read(); -} - -void putchar(unsigned char c) -{ - while (potato_uart_tx_full()) - /* Do Nothing */; - - potato_uart_write(c); -} - -void putstr(const char *str, unsigned long len) -{ - for (unsigned long i = 0; i < len; i++) { - putchar(str[i]); - } -} - -size_t strlen(const char *s) -{ - size_t len = 0; - - while (*s++) - len++; - - return len; -} +#include "console.h" #define HELLO_WORLD "Hello World\r\n" diff --git a/scripts/run_test_console.sh b/scripts/run_test_console.sh new file mode 100755 index 0000000..1aaa2bb --- /dev/null +++ b/scripts/run_test_console.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Runs a test and checks the console output against known good output + +if [ $# -ne 1 ]; then + echo "Usage: run_test.sh " + exit 1 +fi + +TEST=$1 + +TMPDIR=$(mktemp -d) + +function finish { + rm -rf "$TMPDIR" +} + +trap finish EXIT + +MICROWATT_DIR=$PWD + +cd $TMPDIR + +cp ${MICROWATT_DIR}/tests/${TEST}.bin main_ram.bin + +${MICROWATT_DIR}/core_tb > /dev/null 2> test1.out || true + +grep -v "Failed to bind debug socket" test1.out > test.out + +cp ${MICROWATT_DIR}/tests/${TEST}.console_out exp.out + +cp test.out /tmp +cp exp.out /tmp + +diff -q test.out exp.out && echo "$TEST PASS" && exit 0 + +echo "$TEST FAIL ********" +exit 1 diff --git a/tests/Makefile.test b/tests/Makefile.test new file mode 100644 index 0000000..a043810 --- /dev/null +++ b/tests/Makefile.test @@ -0,0 +1,28 @@ +ARCH = $(shell uname -m) +ifneq ("$(ARCH)", "ppc64") +ifneq ("$(ARCH)", "ppc64le") + CROSS_COMPILE = powerpc64le-linux- + endif + endif + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS = -Os -g -Wall -std=c99 -msoft-float -mno-string -mno-multiple -mno-vsx -mno-altivec -mlittle-endian -fno-stack-protector -mstrict-align -ffreestanding -fdata-sections -ffunction-sections -I ../../hello_world +ASFLAGS = $(CFLAGS) +LDFLAGS = -T powerpc.lds + +all: $(TEST).hex + +$(TEST).elf: $(TEST).o head.o ../../hello_world/console.o + $(LD) $(LDFLAGS) -o $(TEST).elf $(TEST).o head.o ../../hello_world/console.o + +$(TEST).bin: $(TEST).elf + $(OBJCOPY) -O binary $(TEST).elf $(TEST).bin + +$(TEST).hex: $(TEST).bin + ../../scripts/bin2hex.py $(TEST).bin > $(TEST).hex + +clean: + @rm -f *.o $(TEST).elf $(TEST).bin $(TEST).hex diff --git a/tests/decrementer/Makefile b/tests/decrementer/Makefile new file mode 100644 index 0000000..2effce5 --- /dev/null +++ b/tests/decrementer/Makefile @@ -0,0 +1,3 @@ +TEST=decrementer + +include ../Makefile.test diff --git a/tests/decrementer/decrementer.c b/tests/decrementer/decrementer.c new file mode 100644 index 0000000..36ac922 --- /dev/null +++ b/tests/decrementer/decrementer.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include "console.h" + +#define TEST "Test " +#define PASS "PASS\r\n" +#define FAIL "FAIL\r\n" + +extern int dec_test_1(void); +extern int dec_test_2(void); +extern int dec_test_3(void); + +// i < 100 +void print_test_number(int i) +{ + putstr(TEST, strlen(TEST)); + putchar(48 + i/10); + putchar(48 + i%10); + putstr(":", 1); +} + +int main(void) +{ + int fail = 0; + + potato_uart_init(); + + print_test_number(1); + if (dec_test_1() != 0) { + fail = 1; + putstr(FAIL, strlen(FAIL)); + } else + putstr(PASS, strlen(PASS)); + + print_test_number(2); + if (dec_test_2() != 0) { + fail = 1; + putstr(FAIL, strlen(FAIL)); + } else + putstr(PASS, strlen(PASS)); + + + print_test_number(3); + if (dec_test_3() != 0) { + fail = 1; + putstr(FAIL, strlen(FAIL)); + } else + putstr(PASS, strlen(PASS)); + + return fail; +} diff --git a/tests/decrementer/head.S b/tests/decrementer/head.S new file mode 100644 index 0000000..11107ca --- /dev/null +++ b/tests/decrementer/head.S @@ -0,0 +1,161 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x2000 + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* + * Microwatt currently enters in LE mode at 0x0, so we don't need to + * do any endian fix ups + */ + . = 0 +.global _start +_start: + b boot_entry + +.global boot_entry +boot_entry: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12 + bctrl + attn // terminate on exit + b . + +/* test to see if end up at the instruction after the rfid */ +.global dec_test_1 +dec_test_1: + li %r3, 0 + mtdec %r3 + LOAD_IMM64(%r3, dec_test_1_rfid) + mtsrr0 %r3 + LOAD_IMM64(%r3, 0x9000000000008003) + mtsrr1 %r3 + li %r3, 1 // preload with a fail code + rfid +dec_test_1_rfid: + b 1f // should be skipped + b 1f // should be skipped + li %r3, 0 + blr +1: + li %r3, 1 + blr + + +/* test to see if end up at the instruction after the rfid */ +.global dec_test_2 +dec_test_2: + li %r3, 0x100 + mtdec %r3 + LOAD_IMM64(%r3, dec_test_2_rfid) + mtsrr0 %r3 + LOAD_IMM64(%r3, 0x9000000000008003) + mtsrr1 %r3 + li %r3, 1 // preload with a fail code + rfid +dec_test_2_rfid: + b . // should wait here and then be skipped + b 1f // should be skipped + li %r3, 0 + blr +1: + li %r3, 1 + blr + + +/* test mtmsrd */ +.global dec_test_3 +dec_test_3: + LOAD_IMM64(%r3, 0x9000000000000003) + mtmsrd %r3 // EE off + li %r3, 0x0 // shouldn't fire right away + mtdec %r3 + b 2f + nop + b 1f // shouln't get here +2: + LOAD_IMM64(%r3, 0x9000000000008003) + mtmsrd %r3 // EE on + b 1f // shouln't get here + b 1f // shouln't get here + + li %r3, 0 + blr +1: + li %r3, 1 + blr + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + EXCEPTION(0x500) + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + + . = 0x900 + mtsprg0 %r3 + LOAD_IMM64(%r3, 0x000000000ffffff) + mtdec %r3 + mfsrr0 %r3 + addi %r3, %r3, 8 // skip 2 instructions + mtsrr0 %r3 + mfsprg0 %r3 + rfid + + 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) +#if 0 + EXCEPTION(0x1000) + EXCEPTION(0x1100) + EXCEPTION(0x1200) + EXCEPTION(0x1300) + EXCEPTION(0x1400) + EXCEPTION(0x1500) + EXCEPTION(0x1600) +#endif + + diff --git a/tests/decrementer/powerpc.lds b/tests/decrementer/powerpc.lds new file mode 100644 index 0000000..0b65470 --- /dev/null +++ b/tests/decrementer/powerpc.lds @@ -0,0 +1,13 @@ +SECTIONS +{ + _start = .; + . = 0; + .head : { + KEEP(*(.head)) + } + . = 0x1000; + .text : { *(.text) } + . = 0x2000; + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/tests/illegal/Makefile b/tests/illegal/Makefile new file mode 100644 index 0000000..1c4f392 --- /dev/null +++ b/tests/illegal/Makefile @@ -0,0 +1,3 @@ +TEST=illegal + +include ../Makefile.test diff --git a/tests/illegal/head.S b/tests/illegal/head.S new file mode 100644 index 0000000..5446d68 --- /dev/null +++ b/tests/illegal/head.S @@ -0,0 +1,125 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x2000 + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* + * Microwatt currently enters in LE mode at 0x0, so we don't need to + * do any endian fix ups + */ + . = 0 +.global _start +_start: + b boot_entry + +.global boot_entry +boot_entry: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12 + bctrl + attn // terminate on exit + b . + +/* test illegal */ +.global ill_test_1 +ill_test_1: + li %r3, 1 // preload a failure + .long 0x0 // illegal will be skipped + 1 more + b 1f // skipped + + li %r3, 1 // preload a failure + .long 0x0 // illegal will be skipped + 1 more + b 1f // skipped + + li %r3, 0 + blr +1: + li %r3, 1 + blr + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + EXCEPTION(0x500) + EXCEPTION(0x600) + + . = 0x700 + mtsprg0 %r3 + mtsprg1 %r4 + + // test for bit 44 being set for ILL + mfsrr1 %r3 + li %r4, 1 + sldi %r4, %r4, (63-44) + and. %r4, %r4, %r3 + li %r4, 8 // PASS so skip 2 instructions + bne 1f + li %r4, 4 // FAIL so only skip 1 instruction. Return will catch +1: + mfsrr0 %r3 + add %r3, %r3, %r4 // skip some instructions + mtsrr0 %r3 + + mfsprg0 %r3 + mfsprg1 %r4 + rfid + + 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) +#if 0 + EXCEPTION(0x1000) + EXCEPTION(0x1100) + EXCEPTION(0x1200) + EXCEPTION(0x1300) + EXCEPTION(0x1400) + EXCEPTION(0x1500) + EXCEPTION(0x1600) +#endif + + diff --git a/tests/illegal/illegal.c b/tests/illegal/illegal.c new file mode 100644 index 0000000..0778ffe --- /dev/null +++ b/tests/illegal/illegal.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "console.h" + +#define TEST "Test " +#define PASS "PASS\r\n" +#define FAIL "FAIL\r\n" + +extern int ill_test_1(void); + +// i < 100 +void print_test_number(int i) +{ + putstr(TEST, strlen(TEST)); + putchar(48 + i/10); + putchar(48 + i%10); + putstr(":", 1); +} + +int main(void) +{ + int fail = 0; + + potato_uart_init(); + + print_test_number(1); + if (ill_test_1() != 0) { + fail = 1; + putstr(FAIL, strlen(FAIL)); + } else + putstr(PASS, strlen(PASS)); + + return fail; +} diff --git a/tests/illegal/powerpc.lds b/tests/illegal/powerpc.lds new file mode 100644 index 0000000..0b65470 --- /dev/null +++ b/tests/illegal/powerpc.lds @@ -0,0 +1,13 @@ +SECTIONS +{ + _start = .; + . = 0; + .head : { + KEEP(*(.head)) + } + . = 0x1000; + .text : { *(.text) } + . = 0x2000; + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/tests/sc/Makefile b/tests/sc/Makefile new file mode 100644 index 0000000..a2fb869 --- /dev/null +++ b/tests/sc/Makefile @@ -0,0 +1,3 @@ +TEST=sc + +include ../Makefile.test diff --git a/tests/sc/head.S b/tests/sc/head.S new file mode 100644 index 0000000..ddbedef --- /dev/null +++ b/tests/sc/head.S @@ -0,0 +1,111 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x2000 + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* + * Microwatt currently enters in LE mode at 0x0, so we don't need to + * do any endian fix ups + */ + . = 0 +.global _start +_start: + b boot_entry + +.global boot_entry +boot_entry: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12 + bctrl + attn // terminate on exit + b . + +/* test sc */ +.global sc_test_1 +sc_test_1: + li %r3, 1 // preload a failure + sc + b 1f // skipped + b 1f // skipped + b 2f // return here is good! + b 1f // shouldn't hit here +2: + li %r3, 0 + blr +1: + li %r3, 1 + blr + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + EXCEPTION(0x500) + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + EXCEPTION(0x900) + EXCEPTION(0x980) + EXCEPTION(0xa00) + EXCEPTION(0xb00) + + . = 0xc00 + mtsprg0 %r3 + mfsrr0 %r3 + addi %r3, %r3, 8 // skip 2 instructions + mtsrr0 %r3 + mfsprg0 %r3 + rfid + + EXCEPTION(0xd00) + EXCEPTION(0xe00) + EXCEPTION(0xe20) + EXCEPTION(0xe40) + EXCEPTION(0xe60) + EXCEPTION(0xe80) + EXCEPTION(0xf00) + EXCEPTION(0xf20) + EXCEPTION(0xf40) + EXCEPTION(0xf60) + EXCEPTION(0xf80) +#if 0 + EXCEPTION(0x1000) + EXCEPTION(0x1100) + EXCEPTION(0x1200) + EXCEPTION(0x1300) + EXCEPTION(0x1400) + EXCEPTION(0x1500) + EXCEPTION(0x1600) +#endif + + diff --git a/tests/sc/powerpc.lds b/tests/sc/powerpc.lds new file mode 100644 index 0000000..0b65470 --- /dev/null +++ b/tests/sc/powerpc.lds @@ -0,0 +1,13 @@ +SECTIONS +{ + _start = .; + . = 0; + .head : { + KEEP(*(.head)) + } + . = 0x1000; + .text : { *(.text) } + . = 0x2000; + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/tests/sc/sc.c b/tests/sc/sc.c new file mode 100644 index 0000000..67d80b9 --- /dev/null +++ b/tests/sc/sc.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "console.h" + +#define TEST "Test " +#define PASS "PASS\r\n" +#define FAIL "FAIL\r\n" + +extern int sc_test_1(void); + +// i < 100 +void print_test_number(int i) +{ + putstr(TEST, strlen(TEST)); + putchar(48 + i/10); + putchar(48 + i%10); + putstr(":", 1); +} + +int main(void) +{ + int fail = 0; + + potato_uart_init(); + + print_test_number(1); + if (sc_test_1() != 0) { + fail = 1; + putstr(FAIL, strlen(FAIL)); + } else + putstr(PASS, strlen(PASS)); + + return fail; +} diff --git a/tests/test_decrementer.bin b/tests/test_decrementer.bin new file mode 100755 index 0000000..9524469 Binary files /dev/null and b/tests/test_decrementer.bin differ diff --git a/tests/test_decrementer.console_out b/tests/test_decrementer.console_out new file mode 100644 index 0000000..e70abe3 --- /dev/null +++ b/tests/test_decrementer.console_out @@ -0,0 +1,3 @@ +Test 01:PASS +Test 02:PASS +Test 03:PASS diff --git a/tests/test_illegal.bin b/tests/test_illegal.bin new file mode 100755 index 0000000..dac5b01 Binary files /dev/null and b/tests/test_illegal.bin differ diff --git a/tests/test_illegal.console_out b/tests/test_illegal.console_out new file mode 100644 index 0000000..f954c28 --- /dev/null +++ b/tests/test_illegal.console_out @@ -0,0 +1 @@ +Test 01:PASS diff --git a/tests/test_sc.bin b/tests/test_sc.bin new file mode 100755 index 0000000..5af07be Binary files /dev/null and b/tests/test_sc.bin differ diff --git a/tests/test_sc.console_out b/tests/test_sc.console_out new file mode 100644 index 0000000..f954c28 --- /dev/null +++ b/tests/test_sc.console_out @@ -0,0 +1 @@ +Test 01:PASS diff --git a/tests/update_console_tests b/tests/update_console_tests new file mode 100755 index 0000000..c17c12b --- /dev/null +++ b/tests/update_console_tests @@ -0,0 +1,14 @@ +#!/bin/bash +# +# Script to update console related tests from source +# + +for i in sc illegal decrementer ; do + cd $i + make + cd - + cp $i/$i.bin test_$i.bin + ln -s test_$i.bin main_ram.bin + ../core_tb > /dev/null 2> test_$i.console_out + rm main_ram.bin +done