core: Add framework for an FPU
This adds the skeleton of a floating-point unit and implements the mffs and mtfsf instructions. Execute1 sends FP instructions to the FPU and receives busy, exception, FP interrupt and illegal interrupt signals from it. Signed-off-by: Paul Mackerras <paulus@ozlabs.org>pull/245/head
parent
76ec1a2f0a
commit
856e9e955f
@ -0,0 +1,185 @@
|
||||
-- Floating-point unit for Microwatt
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
library work;
|
||||
use work.insn_helpers.all;
|
||||
use work.decode_types.all;
|
||||
use work.crhelpers.all;
|
||||
use work.helpers.all;
|
||||
use work.common.all;
|
||||
|
||||
entity fpu is
|
||||
port (
|
||||
clk : in std_ulogic;
|
||||
rst : in std_ulogic;
|
||||
|
||||
e_in : in Execute1toFPUType;
|
||||
e_out : out FPUToExecute1Type;
|
||||
|
||||
w_out : out FPUToWritebackType
|
||||
);
|
||||
end entity fpu;
|
||||
|
||||
architecture behaviour of fpu is
|
||||
|
||||
type state_t is (IDLE,
|
||||
DO_MFFS, DO_MTFSF);
|
||||
|
||||
type reg_type is record
|
||||
state : state_t;
|
||||
busy : std_ulogic;
|
||||
instr_done : std_ulogic;
|
||||
do_intr : std_ulogic;
|
||||
op : insn_type_t;
|
||||
insn : std_ulogic_vector(31 downto 0);
|
||||
dest_fpr : gspr_index_t;
|
||||
fe_mode : std_ulogic;
|
||||
rc : std_ulogic;
|
||||
is_cmp : std_ulogic;
|
||||
single_prec : std_ulogic;
|
||||
fpscr : std_ulogic_vector(31 downto 0);
|
||||
b : std_ulogic_vector(63 downto 0);
|
||||
writing_back : std_ulogic;
|
||||
cr_result : std_ulogic_vector(3 downto 0);
|
||||
cr_mask : std_ulogic_vector(7 downto 0);
|
||||
end record;
|
||||
|
||||
signal r, rin : reg_type;
|
||||
|
||||
signal fp_result : std_ulogic_vector(63 downto 0);
|
||||
|
||||
begin
|
||||
fpu_0: process(clk)
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if rst = '1' then
|
||||
r.state <= IDLE;
|
||||
r.busy <= '0';
|
||||
r.instr_done <= '0';
|
||||
r.do_intr <= '0';
|
||||
r.fpscr <= (others => '0');
|
||||
r.writing_back <= '0';
|
||||
else
|
||||
assert not (r.state /= IDLE and e_in.valid = '1') severity failure;
|
||||
r <= rin;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
e_out.busy <= r.busy;
|
||||
e_out.exception <= r.fpscr(FPSCR_FEX);
|
||||
e_out.interrupt <= r.do_intr;
|
||||
|
||||
w_out.valid <= r.instr_done and not r.do_intr;
|
||||
w_out.write_enable <= r.writing_back;
|
||||
w_out.write_reg <= r.dest_fpr;
|
||||
w_out.write_data <= fp_result;
|
||||
w_out.write_cr_enable <= r.instr_done and r.rc;
|
||||
w_out.write_cr_mask <= r.cr_mask;
|
||||
w_out.write_cr_data <= r.cr_result & r.cr_result & r.cr_result & r.cr_result &
|
||||
r.cr_result & r.cr_result & r.cr_result & r.cr_result;
|
||||
|
||||
fpu_1: process(all)
|
||||
variable v : reg_type;
|
||||
variable illegal : std_ulogic;
|
||||
variable j, k : integer;
|
||||
variable flm : std_ulogic_vector(7 downto 0);
|
||||
begin
|
||||
v := r;
|
||||
illegal := '0';
|
||||
v.busy := '0';
|
||||
|
||||
-- capture incoming instruction
|
||||
if e_in.valid = '1' then
|
||||
v.insn := e_in.insn;
|
||||
v.op := e_in.op;
|
||||
v.fe_mode := or (e_in.fe_mode);
|
||||
v.dest_fpr := e_in.frt;
|
||||
v.single_prec := e_in.single;
|
||||
v.rc := e_in.rc;
|
||||
v.is_cmp := e_in.out_cr;
|
||||
v.cr_mask := num_to_fxm(1);
|
||||
v.b := e_in.frb;
|
||||
end if;
|
||||
|
||||
v.writing_back := '0';
|
||||
v.instr_done := '0';
|
||||
|
||||
case r.state is
|
||||
when IDLE =>
|
||||
if e_in.valid = '1' then
|
||||
case e_in.insn(5 downto 1) is
|
||||
when "00111" =>
|
||||
if e_in.insn(8) = '0' then
|
||||
v.state := DO_MFFS;
|
||||
else
|
||||
v.state := DO_MTFSF;
|
||||
end if;
|
||||
when others =>
|
||||
illegal := '1';
|
||||
end case;
|
||||
end if;
|
||||
|
||||
when DO_MFFS =>
|
||||
v.writing_back := '1';
|
||||
case r.insn(20 downto 16) is
|
||||
when "00000" =>
|
||||
-- mffs
|
||||
when others =>
|
||||
illegal := '1';
|
||||
end case;
|
||||
v.instr_done := '1';
|
||||
v.state := IDLE;
|
||||
|
||||
when DO_MTFSF =>
|
||||
if r.insn(25) = '1' then
|
||||
flm := x"FF";
|
||||
elsif r.insn(16) = '1' then
|
||||
flm := x"00";
|
||||
else
|
||||
flm := r.insn(24 downto 17);
|
||||
end if;
|
||||
for i in 0 to 7 loop
|
||||
k := i * 4;
|
||||
if flm(i) = '1' then
|
||||
v.fpscr(k + 3 downto k) := r.b(k + 3 downto k);
|
||||
end if;
|
||||
end loop;
|
||||
v.instr_done := '1';
|
||||
v.state := IDLE;
|
||||
|
||||
end case;
|
||||
|
||||
-- Data path.
|
||||
-- Just enough to read FPSCR for now.
|
||||
fp_result <= x"00000000" & r.fpscr;
|
||||
|
||||
v.fpscr(FPSCR_VX) := (or (v.fpscr(FPSCR_VXSNAN downto FPSCR_VXVC))) or
|
||||
(or (v.fpscr(FPSCR_VXSOFT downto FPSCR_VXCVI)));
|
||||
v.fpscr(FPSCR_FEX) := or (v.fpscr(FPSCR_VX downto FPSCR_XX) and
|
||||
v.fpscr(FPSCR_VE downto FPSCR_XE));
|
||||
if r.rc = '1' then
|
||||
v.cr_result := v.fpscr(FPSCR_FX downto FPSCR_OX);
|
||||
end if;
|
||||
|
||||
if illegal = '1' then
|
||||
v.instr_done := '0';
|
||||
v.do_intr := '0';
|
||||
v.writing_back := '0';
|
||||
v.busy := '0';
|
||||
v.state := IDLE;
|
||||
else
|
||||
v.do_intr := v.instr_done and v.fpscr(FPSCR_FEX) and r.fe_mode;
|
||||
if v.state /= IDLE or v.do_intr = '1' then
|
||||
v.busy := '1';
|
||||
end if;
|
||||
end if;
|
||||
|
||||
rin <= v;
|
||||
e_out.illegal <= illegal;
|
||||
end process;
|
||||
|
||||
end architecture behaviour;
|
Loading…
Reference in New Issue