-------------------------------------------------------------------------- -- Project: JCPU, a portable 8-bit RISC CPU written in VHDL -- This file: The arithmetic logic unit -- -- Copyright (C) 2007 J. Ahrensfeld -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -- -- For questions and ideas, please contact the author at jens@jayfield.org -- -------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.numeric_std.ALL; use work.cpu_pkg.all; entity alu is Generic ( data_width : integer := 8 ); Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; op_en : in STD_LOGIC; cy_in : in STD_LOGIC; op1_in : in unsigned (data_width-1 downto 0); op2_in : in unsigned (data_width-1 downto 0); opsel : in alu_op_t; res_out : out unsigned (data_width-1 downto 0); stat_load : in STD_LOGIC; stat_in : in alu_status_t; stat_out : out alu_status_t ); end alu; architecture Behavioral of alu is signal sum_cy, sh_cy, sum_cy_in, sh_cy_in, left_shift, add_subn : STD_LOGIC; signal result, sum_res, lf_res, sh_res, res_reg : unsigned (data_width-1 downto 0); signal status, stat_reg : alu_status_t; -------------------------------------------------------------------------- function test_zero(arg : unsigned) return STD_LOGIC is variable result : STD_LOGIC; begin result := '0'; for i in arg'range loop result := result or To_X01(arg(i)); end loop; return not result; end test_zero; -------------------------------------------------------------------------- begin -------------------------------------------------------------------------- proc_logic_func: process (op1_in, op2_in, opsel) begin case opsel is when op1_and_op2 => lf_res <= op1_in and op2_in; when op1_or_op2 => lf_res <= op1_in or op2_in; when op1_xor_op2 => lf_res <= op1_in xor op2_in; when others => lf_res <= (others => '-'); end case; end process; -------------------------------------------------------------------------- proc_shift_func: process (op1_in, left_shift, sh_cy_in) begin if left_shift = '1' then sh_res <= op1_in(op1_in'left-1 downto op1_in'right) & sh_cy_in; sh_cy <= op1_in(op1_in'left); else sh_res <= sh_cy_in & op1_in(op1_in'left downto op1_in'right+1); sh_cy <= op1_in(op1_in'right); end if; end process; -------------------------------------------------------------------------- proc_alu_out: process(opsel, op1_in, op2_in, res_reg, stat_reg, lf_res, sum_res, sh_res, sum_cy, sh_cy, cy_in) begin case opsel is when op1_add_op2 | op1_sub_op2 | op1_addc_op2 | op1_subc_op2 => status.zero <= test_zero(res_reg); status.carry <= sum_cy; result <= sum_res; when op1_and_op2 | op1_or_op2 | op1_xor_op2 => status.zero <= test_zero(res_reg); status.carry <= stat_reg.carry; result <= lf_res; when shl_op | shr_op | rol_op | ror_op | rolc_op | rorc_op => status.zero <= test_zero(res_reg); status.carry <= sh_cy; result <= sh_res; when swap_op => result <= op1_in(op1_in'left-op1_in'length/2 downto op1_in'right) & op1_in(op1_in'left downto op1_in'right+op1_in'length/2); status <= stat_reg; when others => result <= op2_in; status.zero <= stat_reg.zero; status.carry <= stat_reg.carry or cy_in; end case; end process; -------------------------------------------------------------------------- proc_alu_in: process(opsel, op1_in, stat_reg) begin add_subn <= '-'; sum_cy_in <= '-'; sh_cy_in <= '-'; left_shift <= '-'; case opsel is when op1_add_op2 => sum_cy_in <= '0'; add_subn <= '1'; when op1_sub_op2 => sum_cy_in <= '0'; add_subn <= '0'; when op1_addc_op2 => sum_cy_in <= stat_reg.carry; add_subn <= '1'; when op1_subc_op2 => sum_cy_in <= stat_reg.carry; add_subn <= '0'; when shl_op => sh_cy_in <= '0'; left_shift <= '1'; when shr_op => sh_cy_in <= '0'; left_shift <= '0'; when rol_op => sh_cy_in <= op1_in(op1_in'left); left_shift <= '1'; when ror_op => sh_cy_in <= op1_in(op1_in'right); left_shift <= '0'; when rolc_op => sh_cy_in <= stat_reg.carry; left_shift <= '1'; when rorc_op => sh_cy_in <= stat_reg.carry; left_shift <= '0'; when others => null; end case; end process; -------------------------------------------------------------------------- proc_res_reg: process(clk, rst, op_en, stat_load, res_reg, stat_reg) begin if rst = '1' then res_reg <= (others => '0'); stat_reg <= (others => '0'); elsif rising_edge(clk) then if op_en = '1' then res_reg <= result; stat_reg <= status; elsif stat_load = '1' then stat_reg <= stat_in; end if; end if; res_out <= res_reg; stat_out <= stat_reg; end process; -------------------------------------------------------------------------- alu_addsub: process(op1_in, op2_in, sum_cy_in, add_subn) variable sum : unsigned(data_width+1 downto 0); variable op1, op2 : unsigned(data_width+1 downto 0); begin if add_subn = '1' then op1 := '0' & op1_in & '1'; op2 := '0' & op2_in & sum_cy_in; sum := op1 + op2; else op1 := '0' & op1_in & not sum_cy_in; op2 := '0' & op2_in & '1'; sum := op1 - op2; end if; -- Form sum + carry sum_res <= sum(data_width downto 1); sum_cy <= sum(sum'left); end process; -------------------------------------------------------------------------- end Behavioral;